
同时组件又有单文件组件( 真实开发玩的 ) 和 非单文件组件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>玩一下组件</title> <script src="../../js/vue.js"></script> </head> <body> <!-- 被 vm 实例所控制的区域 --> <div id="app"> <!-- 3、使用组件 --> <person></person> <hr/> <hobbys></hobbys> </div> <script> // 去除浏览器控制台中的警告提示信息 Vue.config.productionTip = false; // 玩组件三板斧 // 1、创建组件 const person = Vue.extend({ // 这里在基础篇中怎么玩就怎么玩,相应的也有watch、computed..... // 但是:切记:不可以用el和data必须是函数式 /* 不可以用el的原因是:el指向的是具体的容器,这是根组件做的事情,现在这是是小弟 不可以用data对象式,而必须用函数式:是因为对象是一个引用地址嘛( 玩java的人很熟悉这个对象的事情 ) 如果用对象一是Vue直接不编译、报错,二是就算可以用对象式,那几个变量都可以 指向同一个对象了,因此就会产生:一个变量修改了对象中的东西,那么另一个变量指向 的是同一个对象,因此:这个变量指向的数据也会发生改变 而函数式则不会,因为:函数式就是哪个变量用的,里面的return返回值就属于哪个变量 */ // 使用模板,这个就需要直接写在组件里面了( 让当前组件的内容显示出来嘛 ),如果:放到div容器的模板中,是会报错的 template: ` <div> <h2>{{name}}</h2> <h2>{{age}}</h2> <h2>{{sex}}</h2> </div> `, // 切记:这里是使用data的另一种写法 —— 函数式,必须用 data(){ return { name: '紫邪情', age: 18, sex: '女' } } }) // 再创建一个组件 const hobbys = Vue.extend({ template: ` <div> <h2>{{one}}</h2> <h2>{{two}}</h2> <h2>{{three}}</h2> </div> `, data(){ return { one: '抠脚', two: '玩', three: '酒吧' } } }) // 创建 vm 实例对象 const vm = new Vue({ // 指定控制的区域 el:'#app', // 这里面也可以使用这个编写data,当然也可以不写,里面要写哪些和以前一样 data:{}, // 2、注册组件 components: { // 前为 正式在页面中用的组件名( div模板中用的名字 ) 后为组件所在位置 // person: person, // 这种同名的就可以简写 // 简写 person, hobbys } }); </script> </body> </html>
组件小结

<person></person>1、创建组件时的简写问题
// 1、创建局部组件( 完整写法 ) const person = Vue.extend({ template: ` <div> <h2>{{name}}</h2> <h2>{{age}}</h2> </div> `, data(){ return { name: '紫邪情', age: '女' } } }) // 简写形式 const person2 = { template: ` <div> <h2>{{name}}</h2> <h2>{{age}}</h2> </div> `, data(){ return { name: '紫邪情', age: '女' } } }上面两种都可以




2、组件名的问题







3、关于组件在使用时的注意项
<my-info></my-info>,这种肯定没任何问题<my-info/>,但是这种有坑,这种使用方式需要脚手架支持,否则渲染时会出问题



<my-info></my-info><my-info/> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>组件嵌套</title> <script src="../../js/vue.js"></script> </head> <body> <!-- 被 vm 实例所控制的区域 --> <div id="app"> <!-- 3、使用组件 --> <info></info> </div> <script> // 去除浏览器控制台中的警告提示信息 Vue.config.productionTip = false; // 1、定义组件 const person = { template: ` <div> <h2>{{name}}</h2> <h2>{{sex}}</h2> </div> `, data(){ return { name: '紫邪情', sex: '女' } } } const info = Vue.extend({ template: ` <div> <h2>{{address}}</h2> <h2>{{job}}</h2> <!-- 这个组件中使用被嵌套的组件 --> <person></person> </div> `, data(){ return { address: '浙江杭州', job: 'java' } }, // 基础组件嵌套 —— 这个组件中嵌套person组件 /* 注意前提:被嵌套的组件 需要比 当前嵌套组件先定义( 如:person组件是在info组件前面定义的 ) 原因:因为Vue解析模板时,会按照代码顺序解析,如果定义顺序反了 就会出现:这里用到的组件 在 解析时由于在后面还未解析从而出现找不到 */ components: { person, } }) // 创建 vm 实例对象 const vm = new Vue({ // 指定控制的区域 el:'#app', data:{}, // 2、注册组件 —— 由于info组件中 嵌套了 person组件,所以在这里只需要注册 info组件即可 components: { info, } }); </script> </body> </html>
另一种嵌套:开发中玩的,我的快捷模板给那个div容器起的id值为app,是有用的
在开发中的嵌套是一个vm管理独一无二的app( 就是application 应用的意思 ),然后由app管理众多小弟

所以,现在来玩一下这种组件嵌套
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>vm管app,app管众多组件</title> <script src="../../js/vue.js"></script> </head> <body> <!-- 被 vm 实例所控制的区域 --> <div id="app"></div> <script> // 去除浏览器控制台中的警告提示信息 Vue.config.productionTip = false; // 1、定义组件 const person = { template: ` <div> <h2>{{name}}</h2> <h2>{{sex}}</h2> </div> `, data(){ return { name: '紫邪情', sex: '女' } } } const info = Vue.extend({ template: ` <div> <h2>{{address}}</h2> <h2>{{job}}</h2> <!-- 这个组件中使用被嵌套的组件 --> <person></person> </div> `, data(){ return { address: '浙江杭州', job: 'java' } }, components: { person, } }) // 再定义一个app组件,用来管理其他组件 const app = { // 这个app组件没有其他的东西,就是注册和使用被管理组件而已 components: { // 有其他组件也可以注册在这里面,这里由于info管理了person,所以只注册info即可 info }, template: ` <div> <info></info> </div> `, } // 创建 vm 实例对象 const vm = new Vue({ // 指定控制的区域 el:'#app', data:{}, // 由于组件被app管理,所以:只注册app组件即可 components: { app }, // 使用组件 template: ` <div> <app></app> </div> `, }); </script> </body> </html>
1、来看一下组件到底是谁?
基础代码
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>认识VueComponent</title> <script src="../../js/vue.js"></script> </head> <body> <!-- 被 vm 实例所控制的区域 --> <div id="app"></div> <script> // 去除浏览器控制台中的警告提示信息 Vue.config.productionTip = false; // 1、定义组件 const person = Vue.extend({ template: ` <div> <h2>{{name}}</h2> <h2>{{job}}</h2> <h2>{{address}}</h2> </div> `, data(){ return { name: '紫邪情', job: 'java', address: '浙江杭州' } } }) const app = { components: {person}, template: ` <div> <person></person> </div> `, } // 创建 vm 实例对象 const vm = new Vue({ // 指定控制的区域 el:'#app', data:{}, components: {app}, template: ` <div> <app></app> </div> `, }); </script> </body></html>


既然知道了组件真身是VueComponent(),那么先去源码中看一下它,从而获取到一些对自己有用、能明白的信息

源码提取出来就是下面的样子
var Sub = function VueComponent (options) { this._init(options); <!--里面的重要逻辑封装在了_init()中了,目前不要去看--> }; return Sub };经过前面的分析和查看源码得出两个结论:




VueComponent()小结
组件本质是一个名为VueComponent()的构造函数,且不是程序员自己定义的,是Vue.extend()生成的
我们只需要写组件标签( <person></person> 或 <person/> ) ,Vue解析组件标签时会帮我们创建组件的实例对象,即:Vue帮我们执行了new VueComponent( { options配置选项 } )
关于this的指向问题
VueComponent实例对象,简称:vc( 或:组件实例对象 )
Vue实例对象,简称:vm
但是:vm和vc也有一个坑


观察结构会发现:vm和vc如出一辙,什么数据代理、数据监测等等,vm有的,vc都有,所以vm中的配置项在vc中都可以配置,但是:vm和vc不能画等号,它们两个不一样
vm和vc之间的内置关系是:VueComponent.prototype._ _proto _ _ === Vue.prototype【这句话的验证自行在控制台输出查看,答案是:true】

以上的内容就属于非单文件组件相关的,接下来就看单文件组件,也是开发中会做的事情

<template> <div > <!-- 这里面就是模板 以前在非单文件组件中用的template选项是怎么写的,这里面就是怎么写的--> <h2>{{name}}</h2> </div></template><script> // 这里面就是交互( data、methods、watch、computed..... ) // 就是非单文件组件中的定义组件/* const person = vue.extend({ // 这里就最好配置name选项了,一般都是当前创建的xxxx.vue中的xxxx名字即可 name: 'Person', data() { return { name: '紫邪情' } }, // 这里面还可以写什么methods、watch.....之类的 })*/ // 但是:上面是对照非单文件组件来写的,在这个单文件中其实换了一下下 // 1、这个组件是可以在其他地方复用的,所以:需要把这个组件暴露出去,然后在需要的地方引入即可 /* 这里需要使用到js中模块化的知识 export暴露 import引入嘛 但是:export暴露有三种方式 1、分别暴露 export const person = vue.extend({ 配置选项 }), 就是在前面加一个export而已 可是:根据前面非单文件的知识来看,这个是可以进行简写的 export person {} 2、统一暴露 就是单独弄一行代码,然后使用 export { 要进行暴露的名字 },多个使用 , 逗号隔开即可 3、默认暴露( vue中采用的一种,因为引入时简单 ) export default 组件名{ 配置选项 } 但是:组件名就是当前整个文件,所以可以省略 默认暴露引入: import 起个名字 from 它在哪里 而其他的暴露方式在引入时会有点麻烦 */ // 正宗玩法 export default { name: 'Person', data() { return { name: '紫邪情' } }, // 再配置其他需要的东西也行 如:methods、watch、computed..... }</script><style>/* 这里面就是template中的样式编写, 有就写,没有就不写 */ .temp{ color: purple; }</style>

<template> <div> <!-- 3、使用app管理的组件 --> <person></person> </div></template><script> // 1、引入定义的person组件(要是有其他组件要引入那是一样的套路) // 1Person.vue这个名字不正规啊,我只是为了排序才加了一个1 import person from "./1Person.vue" export default { name: 'App' // 2、注册引起来的组件 components: {person} // 完成了引入和注册之后,在这里面就可以用引入的组件了 }</script><style>/* app是为了管理其他所有的组件,所以这个style其实不写也行( 按需要来吧 ) */</style>// 1、引入app组件import App from "./2App.vue"// 2、把app组件和vm进行绑定new Vue({ // 这里面和以前一样写法,当然:这里的el值绑定的是容器id,怕误会改成root也行 el: '#App', components: {App}})<!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"> <title>创建el容器</title> <!-- 记得要引入js,而此时就需要引入两个js,一个是main.js,一个是vue.js 可是:在解析下面的容器时,可能会导致js渲染不及时出问题 因此:引入js最好放在下面容器的后面引入 --></head><body> <div id="App"> <!-- 2、使用app组件,可以在这里使用,也可以不在这里使用 直接在app.vue中使用template选项进行使用 --> <App></App> </div> <!-- 1、引入js vue.js是因为:main.js中new vue()需要它,所以:先引入vue.js【 提前准备好嘛 】 其次再引入main.js --> <script src="../../../js/vue.js"></script> <script src="./3Main.js"></script></body></html>



当然:以上的东西弄完之后,还启动不了,一启动就会报错

cli全称: command line interface 即:命令行接口工具,但是:一般说的都是脚手架,正规名字说起来太官方、绕口
在vue官网有这个脚手架生态

nodejs官网:https://nodejs.org/en/download/
LTS就是稳定版,而CURRENT就是更新版( 新特性就丢在这里面的,可能会出现bug,所以不推荐下载 )
其中下载msi和zip都可以,区别就是msi是直接帮你把环境变量配好了的,当然:也有其他的区别,而zip就是解压之后,自己进到目录下复制路径,然后配置在“系统环境变量”的path目录中即可,这些操作玩java的第一天就弄了jdk的环境变量,所以再熟悉不过了,而如果是前端人员,nodejs早学了,所以可以下滑到到后面全局安装vue cli那里,安装成功之后是如下效果

查看一下是否成功?
进入dos窗口( win+r,输入cmd回车 )

表明成功
但是:现在npm的配置和缓存文件都在C盘用户目录/appdata/roaming/npm 和 appdata/local/npm-cache中的

因此:我们需要去改动这两个地方( 知道了这两个目录,不用改也可以,后面什么事都可以不做的,对后续的操作也没影响 ,嫌找东西时麻烦就可以改 )
1、在安装的nodejs中新建node_global和node_cache两个文件夹( 前为全局配置路径,后为npm缓存路径 )

2、执行如下命令( 路径记得复制成自己的 ),另外记得用管理员权限打开命令行窗口
进行设置C:\WINDOWS\system32>npm config set prefix "D:\install\Nodejs\node_global"C:\WINDOWS\system32>npm config set cache "D:\install\Nodejs\node_cache"检查是否成功C:\WINDOWS\system32>npm config get prefixD:\install\Nodejs\node_globalC:\WINDOWS\system32>npm config get cacheD:\install\Nodejs\node_cache可见成功修改,但是:还需要做最后一步,去改环境变量( 使用msi安装是默认配好了的,但现在我们改动了配置,需要去重新弄一下 )

3、修改环境变量( 后端人员再熟悉不过,如果前端人员不明白的,直接计算机右键,选择属性,选择高级系统设置,选择环境变量就可以了【家庭版和专业版、win10和win7不一样,自行查找】 )



报一堆ERROR错误的解决办法:本质是你安装时弄错了,文件权限不够 / 另外一种也是最常见的一种,就是明明都是正规步骤,但是就是不行,这是因为你自己本身登录的电脑用户权限不够【 自己买电脑之后,创建登录用户时操作有问题 】
想解决,此时:做一个操作即可,回到nodejs安装的根目录

右键选择属性、安全、高级

当然:要是自己的电脑在这个安全界面中,直接编辑权限,然后把“写入权限”√上是可以的,那就直接√上【 我说的是勾上时,不报错、直接出现一个加载过程 / 无任何报错提示的那种 ,若选择勾上、确认时系统有一个什么鬼提示来着,忘球了,反正有个提示就是不允许操作 】,要是不行就接着往后看


确认之后,再使用npm install -g xxx就发现可以了
安装之后,在刚刚新建的node_global和node_cache中是有东西的

如果想要把全局配置恢复为初始化配置的话,也很简单,系统C盘用户目录/.npmrc的文件,删了就可以了

npm install -g cnpm --registry=https://registry.npm.taobao.org 或 npm config set registry http://registry.npm.taobao.org 这两个都可以,如果因为自己电脑原因出现卡顿,进度条走得很慢、或不走了,那直接敲一下回车就可以了( 但:不是绝对好使,可能在你那边就不得吃 )








1、packpage-lock.json

2、package.json









点开它的包说明:就发现引入的是dist/vue.runtime.esm.js

随便选择一个右键在资源管理器中显示,就可以看到文件大小( 可以和vue.js对比,就少了100kb左右而已,少的就是模板解析器,但是:少的这部分用处很大 )

vue为什么要搞出这么多版本?







<App/> 或 <App><App>



我重新复制了一份src文件夹,用的时候把名字改回来就可以了( 需要哪一个就把哪一个改为src,然后执行npm run serve就行了 )

运行效果如下:

现在有一个需求:获取下图中的DOM结构

document.getElementById进行获取,但是:Vue中提供得有ref属性来进行操作:ref属性用来给“元素”或“子组件“注册引用信息( 也就是id属性的替代者 ),所以来见识第一个“元素”注册引用信息( 在HTML元素中这个ref属性就和id属性是一样的效果 )





ref属性小结
<h1 ref="xxx">......<h1> 或 <Person ref="xxx"><Person>
Person.vue组件编写内容
<template> <div> <h2>{{name}}</h2> <h2>{{sex}}</h2> <h2>{{age}}</h2> <h2>{{job}}</h2> <h2>{{address}}</h2> </div></template><script> export default { name: 'person', // 使用props配置,使这个Person组件中的数据从外部传进来( 封装的思想来咯 ) // 第一种方式:只接收数据即可(数组写法) - 此种方式:接收的数据统统都是字符串 props: ['name','sex','age','job','address'] }</script>App.vue组件编写内容
<template> <div> <h1 ref="content">欢迎来到对抗路,对手信息如下</h1> <!-- 使用组件 并 传入数据 --> <Person name="紫邪情" sex="女" age="18" job="java" address="浙江杭州"/> </div></template><script> import Person from "./components/Person.vue" export default { name: 'App', components: {Person}, }</script>ctrl+s重新编译

效果如下



String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组






回到正题,props接收了数据怎么修改它?


功能:让组件接收外部传进来的数据
(1)、传递数据
<Person name="xxxx">(2)、接收数据
1)、只接收
2)、接收数据 + 数据类型限定
props: { name:String }3)、接收数据 + 数据类型限定 + 必要性限制 + 数据默认值
props: { type:String, required:true, default:'紫邪情' 注:一个数据字段不会同时出现required和defautle }注意:props中的数据是只读的,Vue底层会监测对props的修改,如果进行了修改,就会发出警告
基础代码


在上面的代码中,methods中的代码是相同的,因此:使用mixin混入来进行简化,也是三板斧而已



但是:mixin混入有一些注意点





另外:mixin混入是支持全局配置的,不过这种操作不当会出现问题,因此:目前不建议用,需要时再玩吧,思路如下:
功能:把多个组件共同的配置提取成一个混入对象
使用方法:
1、定义混入,如:
暴露方式 const 对象名 { data(){......}, methods:{........}, ........ }2、在需要的组件中引入混入
3、使用混入,如:
使用,也简单,还是三板斧
基础代码效果

1、创建一个包含install()方法的对象的js文件

2、在main.js中引入、使用插件

3、效果如下

正宗玩法




当然:我们也可以给插件中传东西进去



功能:用于增强Vue
本质:是包含install()方法的一个对象的js文件,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
插件的玩法:
1、定义插件:
// 1、创建插件 export default 是自己选择的暴露方式export default { install(Vue,[ other params ]){ // 定义全局过滤器 Vue.filter( ..... ), Vue.directive( ...... ), Vue.mixin( ....... ) ......... }}2、在main.js中引入插件
3、在main.js中向Vue应用插件 Vue.use( 插件名 )
[ 4、使用插件中定义的东西 ] ———— 可有可没有,看自己的代码情况
这里注意一个东西:定义插件中的install()第一个参数是Vue,即:vm的缔造者,Vue构造函数( 这里可以联想原型对象,也就是前面说的vm和vc的内置关系:VueComponent.prototype._ _proto _ _ === Vue.prototype,这也就是说在Vue身上加点东西,那么:vm和vc都可以拿到,如:
Vue.prototype.$myMethod = function(){ ...... } Vue.prototype.$myProperty = xxxx prototype路线是加东西 _ _proto_ _路线是取东西<style scoped>假如有两个组件,里面都用了同一个class类名,但是做的style却是不一样的


此时如果把两个class名字改成一样呢?开发中样式多了这种事情是在所难免的

凭什么就是Person2.vue组件中的样式优先?


那上述样式冲突了怎么解决?


当然:style标签不止支持scoped属性,还可以用其他的

另外:less需要less-loader支持,所以需要安装less-loader,但是:有坑( 版本的问题 )

cli中的webpack版本

安装适合cli的webpack的less-loader版本

最后还有一个问题:scoped使用在App.vue中就会发生很诡异的事情
实例:实现如下的效果( 就是一个人名录入,然后可以对名字做一点操作罢了 )

根据页面结构来看,可以拆分成如下的结构:
1、创建并编写组件对应内容( 统称编写静态组件 )
1)、App组件
(1)、HTML结构
<template> <div id="root"> <div > <div > <NameHeader></NameHeader> <NameList></NameList> <NameFooter></NameFooter> </div> </div> </div></template><script>import NameHeader from "./components/NameHeader.vue"import NameList from "./components/NameList.vue"import NameFooter from "./components/NameFooter.vue" export default { name: 'App', components: {NameHeader,NameList,NameFooter} }</script>(2)、CSS样式 + 后续需要的通用样式
body{ background: #fff;} .btn { display: inline-block; padding: 4px 12px; margin-bottom: 0; font-size: 14px; line-height: 20px; text-align: center; vertical-align: middle; cursor: pointer; box-shadow: inset 0 1px rgba(255,255,255,0.2),0 1px 2px rgba(0, 0, 0, 0.05); border-radius: 4px;}.btn-danger { color: #fff; background-color: #da4f49; border: 1px solid #bd362f;}.btn-danger:hover { color: #fff; background-color: #bd362f;}.btn:focus { outline: none;}.name-container { width: 600px; margin: 0 auto;}.name-container .todo-wrap { padding: 10px; border: 1px solid #ddd; border-radius: 5px;}2)、输入框组件
(1)、HTML结构
<div > <label> <input type="checkbox"> </label> <span> <span>已选择0</span> / 共计2 </span> <button >清除已选人员</button> </div>3)、人名展示框
(1)、HTML结构
<template> <NameObj></NameObj></template><script>import NameObj from "./NameObj.vue" export default { name: 'NameList', components: {NameObj} }</script>(2)、CSS样式
/* #region list */ .name-main { margin-left: 0px; border: 1px solid s#ddd; border-radius: 2px; padding: 0px; } .name-empty { height: 40px; line-height: 40px; border: 1px solid #ddd; border-radius: 2px; padding-left: 5px; margin-top: 10px; }/* #endregion */4、每个人名展示
(1)、HTML结构
<template> <ul > <li> <label> <input type="checkbox"/> <span>xxxxx</span> </label> <button >删除</button> </li> </ul></template><script> export default { name: 'NameObj', }</script>(2)、CSS样式
/* #region item */ li { list-style: none; height: 36px; line-height: 36px; padding: 0 5px; border-bottom: 1px solid #ddd; } li label { float: left; cursor: pointer; } li label li input { vertical-align: middle; margin-right: 6px; position: relative; top: 1px; } li button { float: right; display: none; margin-top: 3px; } li:before { content: initial; } li:last-child { border-bottom: none; }/* #endregion */运行效果如下:

按照分析,要展示的数据是一堆数据,而数据显示的地方是NameList,所以:data数据就放到NameList中去。本实例中数据放到这里有坑啊,但是:先这么放,后续遇到坑了再调整,所以改一下源码

1、父传子



2、获取输入的内容并添加到显示的顶部 —— 和后续知识挂钩的重点来了

3、原生的不同组件通讯









4、将子组件传递的数据添加到数据栏的顶部去


1、实现选择和数据的改变









2、实现每条数据的删除功能






3、实现底部的已选择人数和总计人数功能
3.1、最原生的方式


3.2、使用数组的reduce()这个API,这个API专做数据统计的




接下来就只剩下底部的全选和清除已选这两个功能了
4、实现全选交互和清除已选人员功能
4.1、实现全选交互( 子传父 + 计算属性使用技巧 )





4.2、清除已选人员


组件化编写流程
props配置总结
v-model总结