整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

web前端:vue源码解析,vue-cli父子组件传递模板

.vue-cli安装命令

1.全局安装vue-cli
$ npm install --global vue-cli
2.创建一个基于webpack脚手架的project,cmd进入指定项目目录
[D:\DEV_CODE\html_code\base-vue\vue-cli  进入后cmd]
$ vue init webpack {project-name}

3.进入项目
$ cd {project-name}
4.运行
$ npm run dev

2.Cmd运行记录解析

Microsoft Windows [版本 10.0.18363.1379]
(c) 2019 Microsoft Corporation。保留所有权利。

D:\DEV_CODE\html_code\base-vue\vue-cli>vue init webpack c-todolist

? Project name c-todolist  确认项目名称
? Project description A Vue.js project  确认项目描述
? Author cevent <1540001771@qq.com>  确认作者
? Vue build standalone  标准单机版
? Install vue-router? Yes   是否安装vue-router路由
? Use ESLint to lint your code? Yes  启用ESlint标准
? Pick an ESLint preset Standard
? Set up unit tests No  初始化测试类,全选no
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm  选择安装模式npm(可选yarn)

   vue-cli · Generated "c-todolist".
# Installing project dependencies ...
# ========================
...
added 1376 packages from 715 contributors in 185.663s
38 packages are looking for funding
  run `npm fund` for details
Running eslint --fix to comply with chosen preset rules...
# ========================
> c-todolist@1.0.0 lint D:\DEV_CODE\html_code\base-vue\vue-cli\c-todolist
> eslint --ext .js,.vue src "--fix"
# Project initialization finished!
# ========================

To get started:
  cd c-todolist
  npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack

D:\DEV_CODE\html_code\base-vue\vue-cli>cd c-todolist  进入项目目录
D:\DEV_CODE\html_code\base-vue\vue-cli\c-todolist>tree  以tree模式查看目录结构
卷 APP_SOFT 的文件夹 PATH 列表
卷序列号为 BCA1-A4B1
D:.
├─build
├─config
├─node_modules
│  ├─.bin
│  ├─@babel
│  │  ├─code-frame
│  │  │  └─lib
│  │  ├─generator
│  │  │  ├─lib
│  │  │  │  ├─generators
│  │  │  │  └─node
│  │  │  └─node_modules
│  │  │      ├─.bin
│  │  │      ├─jsesc
.....

D:\DEV_CODE\html_code\base-vue\vue-cli\c-todolist>dir  查看目录下文件
 驱动器 D 中的卷是 APP_SOFT
 卷的序列号是 BCA1-A4B1

 D:\DEV_CODE\html_code\base-vue\vue-cli\c-todolist 的目录

2021/02/24  13:14    <DIR>          .
2021/02/24  13:14    <DIR>          ..
2021/02/24  13:10               230 .babelrc
2021/02/24  13:10               147 .editorconfig
2021/02/24  13:10                30 .eslintignore
2021/02/24  13:10               791 .eslintrc.js
2021/02/24  13:10               154 .gitignore
2021/02/24  13:10               246 .postcssrc.js
2021/02/24  13:10    <DIR>          build
2021/02/24  13:10    <DIR>          config
2021/02/24  13:10               272 index.html
2021/02/24  13:14    <DIR>          node_modules
2021/02/24  13:14           506,953 package-lock.json
2021/02/24  13:10             2,165 package.json
2021/02/24  13:10               467 README.md
2021/02/24  13:10    <DIR>          src
2021/02/24  13:10    <DIR>          static
              10 个文件        511,455 字节
               7 个目录 64,125,562,880 可用字节


vue-app


vue-cli目录解析


3.npm和cnpm区别、参数及安装

npm(node.js package manager)node.js包管理器,用于node插件管理(安装、卸载、依赖管理)。国外服务器

cnpm:国内阿里团队创建的npm镜像

参数

说明

-g

全局安装(global)

查看全局安装的文件夹位置:npm root -g

C:\Users\asus>npm root -g

C:\Users\asus\AppData\Roaming\npm\node_modules

--save(缩写-S)

安装包信息将加入到依赖productDependencies生产阶段(命令不区分大小写)

--save --dev(缩写-D

安装包信息将加入到依赖devDependencies开发阶段+product生产阶段(命令不区分大小写)

install(缩写i)

安装

全局安装cnpm


指定镜像资源

npm install -gd express --registry=http://registry.npm.taobao.org


安装vue-cli


-》npm install -g vue-cli

-》cnpm install -gd vue-cli

如果 vue init webpack {projectName} 失败,则需要重新安装vue-cli

查看npm注册

C:\Users\asus>npm get registry

http://registry.npm.taobao.org/


避免每次安装都要registry参数,可使用永久设置

npm config set registry http://registry.npm.taobao.org

解决:'cnpm' 不是内部或外部命令。


install -g cnpm --registry=https://registry.npm.taobao.org

C:\Users\asus>npm install -g cnpm --registry=https://registry.npm.taobao.org

npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142

npm WARN deprecated har-validator@5.1.5: this

C:\Users\asus\AppData\Roaming\npm\cnpm -> C:\Users\asus\AppData\Roaming\npm\node_modules\cnpm\bin\cnpm

+ cnpm@6.1.1

added 689 packages from 973 contributors in 35.267s


C:\Users\asus>cnpm -v

cnpm@6.1.1 (C:\Users\asus\AppData\Roaming\npm\node_modules\cnpm\lib\parse_argv.js)

npm@6.14.11 (C:\Users\asus\AppData\Roaming\npm\node_modules\cnpm\node_modules\npm\lib\npm.js)

node@12.14.0 (C:\DevTools\nvm-nodejs\node.exe)

npminstall@3.28.0 (C:\Users\asus\AppData\Roaming\npm\node_modules\cnpm\node_modules\npminstall\lib\index.js)

prefix=C:\Users\asus\AppData\Roaming\npm

win32 x64 10.0.18363

registry=https://r.npm.taobao.org


C:\Users\asus>npm -v

6.13.4


C:\Users\asus>cls 命令清理控制台(清屏)


-gd加入全局+开发环境+生产环境配置

cnpm install -gd vue-cli 安装vue-cli

C:\Users\asus>vue -V 注意大写V

@vue/cli 4.4.6


Npm配置初始化

npm init -f


4.vue源码文件解析


vue-source

Vue中index.html引入静态资源,默认build目录下的webpack打包配置,静态资源必须放在static下


vue静态资源配置

5.关闭eslint的no-tabs和indent缩进和no-trailing-spaces


出现运行失败问题

注释eslint规范


eslint

6.vue父子组件传递


父组件结构图


子组件结构图

7.安装插件bootstrap目录,注意引入样式需要放在static目录下


bootstrap


局部样式控制

8.Index.html入口,引入全局css和js

<!DOCTYPE html>
<html lang="en">

	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width,initial-scale=1.0">
		<!--vue中在index.html页面引入bootstrap失败,必须将css、js放在static,源于-->
		<link rel="stylesheet" href="static/base/css/bootstrap.css" />
		<!--vue中引入需要javascript-->
		<script type="javascript" src="static/base/js/jquery.min.js"></script>
		<script type="javascript" src="static/base/js/bootstrap.js"></script>
		<title>c-todolist</title>
	</head>

	<body>
		<!--这里的app指向main.js中自定义的el名-->
		<!--<div id="app"></div>-->
		<div id="todoList"></div>
		<!-- built files will be auto injected -->
	</body>

</html>

9.Main.js引入todoList页面,作为模板

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
// import App from './App'
import router from './router'
import TodoList from './TodoList'

//  Vue.config.productionTip = false;

/* eslint-disable no-new */
new Vue({
	//  el: '#app',
	//  注册的局部组件:App.vue  components:{App(key):App(value)}, 引如的组件如果key=value,可以直接省略为{App}
	//  components: { App },
	//  模板为App.vue的内容
	//  template: '<App/>'
	el: '#todoList',
	router,
	components: {
		TodoList
	},
	template: '<TodoList />'
})

10.TodoList.vue页面,作为父组件

<template>
	<div id="todoList">
		<h3>TodoList页面{{message}}</h3>
		<div id="todoList-group1" class="input-group input-group-lg">
			<span class="input-group-addon" id="sizing-addon1">Table</span>
			<input type="text" class="form-control" placeholder="输入内容" aria-describedby="sizing-addon1" v-model="inputValue">
			<button class="btn btn-success btn-lg" type="button" @click="add">新增</button>
		</div>
		<div id="todoListinput">
			<table class="table" v-for="(item,index) of list" :key="index" :bindx="index">
				<tr class="active">
					<td >
						<span>{{index}}{{item}}</span>
						<button class="btn btn-primary " type="button"  @click="del(bindx)" >刪除</button>
					</td>
				</tr>
			</table>
		</div>
		
		<div id="todoList-group2" class="input-group input-group-lg">
			<span class="input-group-addon" id="sizing-addon1">组件传递Table</span>
			<input type="text" class="form-control" placeholder="输入内容" aria-describedby="sizing-addon1" v-model="inputValueComp">
			<button class="btn btn-danger btn-lg" type="button" @click="addComp">新增</button>
		</div>
		<div id="todoItemList">
			<!--父组件向子组件传值,需要绑定参数给子组件props
				@parentDel在子组件的方法中,里面包含一个变量,是否父组件的itemIndex传入,然后子组件通过this.@emit("parentDel",this.index)的方法,发送到父组件,这个index在父组件方法中可以定位任意参数
			-->
			<todo-item v-for="(item,index) of compList" :key="index" :itemContent="item" :itemIndex="index" @parentDel="delComp"></todo-item>
		</div>
	</div>

</template>

<script>
	//引入组件:当前目录下./
	import TodoItem from './components/TodoItem';
	export default {

		name: 'todoList',
		/* 当前作为组件,data为函数,需要return定义返回值
		 * data:function(){
		 * 	return{
		 * 	
		 * 	}
		 * }
		 * 这里ES6的语法中可做简化
		 * data(){
		 * 	return{}
		 * }
		 */
		/* 非自定义名称集合:['组件名称','组件名称']
		 * 自定义名称: {'todo-item': TodoItem}
		 */
		components: {
			'todo-item': TodoItem,
		},
		data() {
			return {
				message: "欢迎cevent光临寒舍!",
				inputValue: "",
				list: [],
				//组件参数
				compList:[],
				inputValueComp:"",
			}
		},

		methods: {
			//新增事件
			add() {
				console.log("输入的内容:",this.inputValue);
				//这里的this.list=this.$data.list
				this.list.push(this.inputValue);
				this.inputValue = "";
				console.log(":bindx=index",this.list.item);
			},
			del(bindx){
				console.log("删除的内容:",bindx);
				this.list.splice(bindx,1);
			},
			//todoItem组件新增
			addComp(){
				this.compList.push(this.inputValueComp);
				this.inputValueComp="";
			},
			delComp(compIndex){
				this.compList.splice(compIndex,1);
			}
		}
	}
</script>

<style>
	#todoList {
		font-family: 'Avenir', Helvetica, Arial, sans-serif;
		-webkit-font-smoothing: antialiased;
		-moz-osx-font-smoothing: grayscale;
		text-align: center;
		color: #2c3e50;
		margin-top: 40px;
	}
	
	#todoList-group1 {
		width: 50%;
		top: 50%;
		left: 50%;
		transform: translate3d(-50%, -50%, 0);
		margin-top: 50px;
	}
	
	#todoList-group1 button {
		position: absolute;
		margin: 0 5px;
	}
	
	#todoList-group2 {
		width: 50%;
		top: 50%;
		left: 50%;
		transform: translate3d(-50%, -50%, 0);
		margin-top: 50px;
	}
	
	#todoList-group2 button {
		position: absolute;
		margin: 0 5px;
	}
	
	#todoListinput {
		position: relative;
		height: 46px;
		width: 50%;
		height: 100%;
		top: 0;
		bottom: 0;
		left: 0;
		right: 0;
		margin: auto;
		margin-top: -15px;
		border-radius: 2%;
		line-height: 46px;
		color: white;
		font-size: 20px;
		transition: 1s;
		border: 2px solid silver;
	}
	#todoListinput table{
		margin-bottom: 3px;
	}
	#todoListinput table tr{
		position: relative;
		background: salmon;
		padding: 0;
	}
	#todoListinput table tr td span{
		position: relative;
		float: left;
		left: 20%;
	}
	#todoListinput table tr td button{
		position: relative;
		float: right;
		right: 10%;
		margin-top: 1.5%;
	}
	
	#todoItemList{
		
	}
</style>

11.Components中TodoItem.vue页面,作为子组件

<template>
	<table class="todo-table">
		<tr id="tableTR">
			<td> {{itemContent}} {{itemIndex}} <button type="button" class="btn btn-default" @click="compDel">删除</button></td>
		</tr>
	</table>

</template>

<script>
	export default {
		name: 'TodoItem',
		//接收父组件传值
		props:['itemContent','itemIndex'],
		data() {
			return {
				ceventMsg: '这是一个cevent引用的data',
			}
		},
		methods:{
			/**省略写法
			 * xxx:function(){...}
			 */
			compDel(){
				console.log("子组件传给父组件的index:",this.itemIndex);
				//将index发射到父组件,指定接收的方法为"parentDel",这里父组件选用click时间
				this.$emit("parentDel",this.itemIndex);
			}
		}
	}
</script>

<style>
	.todo-table {
		position: relative;
		width: 50%;
		height: 100%;
		top: 50%;
		left: 50%;
		transform: translate3d(-50%, -50%, 0);
		border:1px solid skyblue;
		border-radius: 8px;
		margin-top: 2px;
	}
	/*覆盖哪一层,在哪一层指定transition*/
	.todo-table:hover #tableTR{
		background: deepskyblue;
		color: #000000;
	}
	
	#tableTR {
		position: relative;
		background: steelblue;
		width: 90%;
		
		
		color: white;
		transition: 1s;
		margin-top: 2px;
	}
	
	#tableTR  td {
		position: relative;
		width: 80%;
		margin: 2px auto;
		font-size: 24px;
		float: left;
	}
	
	#tableTR td button{
		position: relative;
		width: 20%;
		float: right;
		right: -25%;
	}
	
</style>

13.效果图


todoList

么是Vue组件通讯 Vue.js 组件通信是指在 Vue 应用的不同组件之间进行数据交换和状态同步的过程。由于 Vue 的组件是基于单文件组件(SFCs)的模块化设计,每个组件都有自己的作用域,因此它们不能直接访问彼此的数据。为了使组件之间能够协同工作,Vue 提供了几种不同的通信方式。以下是 Vue 中常见的组件通信模式:1.父子组件通讯,2.子父组件通讯。

父子组件通讯

简单明了的说父子组件通讯,其实就是父组件通过v-bind指令,将值传给子组件,而子组件使用Vue自带的defineProps接收父组件传过来的值。

我们使用命令行创建一个Vue项目之后

 npm create vue@latest 

App.vue文件中我们开始实现父子组件通讯,我们在src文件夹下的components文件下,创建第一个子组件child.vue.首先我们需要把子组件引入父组件中,在Js部分我们用import引入

    import Child from '@/components/child.vue'
    \\或者 import Child from './components/child.vue'

可以看到,这个界面中有一个input输入框,以及一个列表。我们需要在输入框中,输入信息,就会在列表中显示我们输入的内容。这里既然是父传子,我们就在父组件中写输入框,在子组件写列表就好了。在父组件中我们引入了子组件,引入的名称,就可以当做一个标签使用,用来展示子组件的内容。

<template>
  <div class="input-group">
    <input type="text" v-model="value">
    <button @click="add">添加</button>
  </div>

  <Child :msg="toChild"></Child>

</template>

在input框,我们使用v-model(v-model是Vue.js框架中用于实现表单输入和其他元素的双向数据绑定的指令。)进行数据绑定,使用v-model进行双向绑定,方便我们获取到输入框的数据,并把该数据传给子组件。button按钮,我们使用@click绑定了一个添加函数add()<Child :msg="toChild"></Child> 这就是我们将引入的子组件作为标签使用,可以理解为我们打开了一个门,可以到达子组件。msg是参数名,toChild是参数值,在子组件中我们获取参数名就好了。 现在我们来思考,Js部分我们需要什么,首先我们使用v-modelvalue进行了双向绑定,,也即时说当输入框中的value值发生了改变,我们Js部分的value也需要改变,反过来也是一样的,这里我们就需要使用到一个响应式数据的概念,在vue中带有一个模块叫做ref可以让我们把数据变成响应式数据(也有个一reactive,作用是一样的,但是作用对象不一样)。然后还有一个值叫作toChild传给子组件也是一个响应式数据。在函数add()我们需要的时,把输入框中的数据value传给子组件,而toChild就是传给子组件的值,所以我们只需要把value 赋值给toChild就好了

<script setup>
import Child from './components/child.vue'
import { ref } from 'vue'

const value = ref('')
const toChild = ref('')

const add = () => {
  toChild.value = value.value
}
</script>

那么子组件child.vue又该如何实现呢,在子组件中我们需要展示列表中的数据,这里我们肯定不是一个li一个li的写,可以直接只用到v-for(v-for是Vue.js中的一项非常重要的指令,用于在模板中迭代数组、对象或集合。)指令输出就好了.我们定义一个数组用来存放数据,我们遍历输出数组,同时将父组件的值添加到数组中,所以这边,数组也就会是一个响应式数据了。我们向输入框中输入数据,并添加。这个数组就会改变,但是如果我们不对这个数组进行监听,v-for遍历的数据是不会更新的,在vue存在一个监听watch可以对数据的更新进行监听。

<template>
    <div class="child">
      <ul>
        <li v-for="item in list">{{ item }}</li>
      </ul>
    </div>
  </template>
  
  <script setup>
  import { ref, watch } from 'vue'
  const list = ref(['html', 'css', 'js'])
  const props = defineProps({
    msg: ''
  })
  
  watch(
    () => props.msg,
    (newVal, oldVal) => {
      list.value.push(newVal)
    }
  )
  
  </script>
  
  <style lang="css" scoped></style>

这里我们使用defineProps接受父组件传来的参数.在vue的API文档是这样介绍watch

这样我们就实现了父子组件通讯。

子父组件通讯

子父组件通讯那就是反过来的意思呗,子组件将值传给父组件,例子依旧是这个例子

现在我们在子组件中写输入框,父组件写列表。child2.vue

<template>
    <div class="input-group">
      <input type="text" v-model="value">
      <button @click="add">添加</button>
    </div>
  </template>
  
  <script setup>
  import { ref } from 'vue'
  const value = ref('')
   
  const emits = defineEmits(['add1'])  // 创建一个add事件
  const add = () => {
    // 将value给到父组件
    emits('add1', value.value)  // 发布事件
  }
  </script>
  
  <style lang="css" scoped>
  
  </style>

写法其实没有太大差别,重点是defineEmits(在 Vue 3 中,defineEmits 是一个用于定义组件可以触发的自定义事件的选项。这使得组件的使用者能够明确地知道哪些事件是可以从这个组件中发出的,从而增强了代码的可读性和可维护性。),我们先创建了一个事件'add1',当我们触发事件'add1'就会把事件发布出去,同时会把参数va;ue.value传出去,当谁订阅事件add1,谁就可以拿到参数value.value.父组件App2.vue的写法也很简单

<template>
    <!-- 订阅add1事件 -->
    <Child @add1="handle"></Child>
  
    <div class="child">
      <ul>
        <li v-for="item in list">{{ item }}</li>
      </ul>
    </div>
  
  </template>
  
  <script setup>
  import Child from '@/components/child2.vue'
  import { ref } from 'vue'
  const list = ref(['html', 'css', 'js'])
  
  const handle = (event) => {
    list.value.push(event)
  }
  </script>
  
  <style lang="css" scoped>
  
  </style>

我们需要在<Child/>标签中订阅这个事件就好了,同时触发函数handle,这个函数自带一个形参event,这个形参是指就是事件add1带过来的参数value.value. 当然实现子父组件通讯也可以使用v-model对父组件的值与子组件的值进行双向绑定。 写法跟订阅事件其实差不多App3.vue

<template>
    <Child v-model:list="list"></Child>
  
    <div class="child">
      <ul>
        <li v-for="item in list">{{ item }}</li>
      </ul>
    </div>
  
  </template>
  
  <script setup>
  import Child from '@/components/child3.vue'
  import { ref } from 'vue'
  const list = ref(['html', 'css', 'js'])
  
  </script>
  
  <style lang="css" scoped>
  
  </style>

我们借助v-model对数据list双向绑定,我们在子组件中接收list值,并对它进行数据更新。子组件child3.vue

<template>
    <div class="input-group">
      <input type="text" v-model="value">
      <button @click="add">添加</button>
    </div>
  </template>
  
  <script setup>
  import { ref, defineProps } from 'vue';
  const value = ref('')
  
  const props = defineProps({
    list: {
      type: Array,
      default: () => []
    }
  })
  
  const emits = defineEmits(['update:list'])
  const add = () => {
    const arr = props.list
    arr.push(value.value)
    emits('update:list', arr)
  }
  </script>
  
  <style lang="css" scoped></style>

值得一提的是,在子组件中我们也使用到了defineEmits但是在父组件中,我们并没有订阅事件,这是因为,我们的v-model是双向绑定数据,当子组件中的list的值发生了改变,就会触发v-model,等同于我们触发了订阅。

TML全称是:超文本标记语言(HyperText Markup Language),学习这种语言主要就是学习各种标签的使用,今天给大家介绍HTML的基本标签。

html标签