1、非模块化的缺点:
- 代码杂乱无章,没有条理性,不便于维护,不便于复用
- 很多代码重复、逻辑重复
- 全局变量污染
- 不方便保护私有数据(闭包)
2、常见模块化标准(了解):
CommonJS:Node中使用的模块化解决方案
ES6 Module:ES6模块化
AMD:异步模块定义,其实就是requireJS实现的模块化解决方案
CMD:seajs中提出来的模块化解决方案
3、ES6新增了模块化功能,我们来简要学习下
ES6的模块化分为导出(export)与导入(import)两个模块。
在ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出),想要使用模块,就要使用import进行导入后使用。
案例代码:
1)新建test.js
//导出变量 其实是导出对象:{username:"潘子夜"} export var username="潘子夜"; //默认导出 其实是导出对象:{default:fn} //一个模块只能有一个默认导出,对于默认导出,导入的名称可以和导出的名称不一致。 export default function(a,b){ return a+b; } //具名导出(普通导出)其实是导出对象:{multiply:fn} export function multiply(a,b){ return a*b; } //如果要输出多个变量可以将这些变量包装成对象进行模块化输出 var age = 20; var grade = "大三"; export { age, grade } //如果你不想暴露模块当中的变量名字,可以通过as来进行操作: var mySex = "男"; var myHobby = "打球"; var myFun = function(a,b){ return a-b; } export { mySex as sex, myHobby as hobby, myFun as fun }
2)新建test1.js
export var password="我来自test1.js";
3)新建test2.js
export var password="我来自test2.js";
4)新建test3.js
export var fileName="演示整体导入test3.js"; export function double(a){ return a*2; }
5)新建index.js
进行模块引入
//导入变量 import {username} from './test.js' //导入默认 导入名称随意,注意:没有{} import sum from './test.js' //导入普通 import {multiply} from './test.js' //导入多个变量 import {age,grade} from './test.js' //导入别名 import {sex,hobby,fun} from './test.js' //重命名export和import //如果导入的多个文件中,变量名字相同,即会产生命名冲突的问题,为了解决该问题,ES6为提供了重命名的方法,当你在导入名称时可以这样做: import {password as pwd1} from "./test1.js"; import {password as pwd2} from "./test2.js"; //也可以直接导入整个模块 import * as test3 from "./test3.js" //依次输出 console.log(username); console.log(sum(6,3)); console.log(multiply(6,3)); console.log(age); console.log(grade); console.log(sex); console.log(hobby); console.log(fun(6,3)); console.log(pwd1); console.log(pwd2); console.log(test3.fileName); console.log(test3.double(5));
{}
,不加{}
都是默认导入6)新建index.html
引入index.js
测试
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <!-- type="module"表示以模块方式引入 --> <script src="./js/index.js" type="module"></script> </head> <body> </body> </html>
1、一个完整的网页是复杂的,如果将其作为一个整体来进行开发,将会遇到下面的困难:
- 代码凌乱臃肿
- 不易协作
- 难以复用
2、vue推荐使用-种更加精细的控制方案——组件化开发
所谓组件化,即把一个页面中区域功能细分, 每一个区域成为一个组件,每个组件包含:
- 功能(JS代码)
- 内容(模板代码)
- 样式(CSS代码)
1、组件是根据一个普通的配置对象创建的,所以要开发一个组件, 只需要写一个配置对象即可,该配置对象和vue实例的配置是几乎一样的。
2、Vue创建组件语法:
//自定义组件配置对象 var myComp = { data(){ return { //..... } }, computed: { //... }, methods: { //.... }, template: { //..... } }
3、值得注意的是,每一个组件其实都是一个Vue实例,不过组件配置对象和vue实例有以下几点差异:
- 无
el
data
必须是一个函数,该函数返回的对象作为数据- 由于没有
el
配置,组件的模板必须定义在template
中
组件在使用之前必须要先注册组件,注册组件有两种方式:全局注册和局部注册
1)一旦全局注册了一个组件,整个应用中任何地方都可以使用该组件
2)全局注册方式
//参数1:组件名称,将来在模板中使用组件时,会使用该名称 //参数2:组件配置对象 //该代码运行后,即可在模板中使用组件 Vue.component(' my-comp' ,myComp )
3)注册好的组件使用
上面的组件注册好后,在模板中,就可以使用组件了,使用格式:
<my-comp /> <!--或--> <my-comp></my-comp>
在一些工程化的大型项目中, 很多组件都不需要全局使用。
比如一个登录组件,只有在登录的相关页面中使用,如果全局注册,将导致构建工具无法优化打包。
因此,除非组件特别通用,否则不建议使用全局注册。
4)代码案例:
<div id="app"> {{title}} <ul> <!-- 应用组件-两种写法 --> <my-comp></my-comp> <my-comp/> </ul> </div> <script> //自定义组件 var myComp = { data(){ return { name:"潘子夜", age:20 } }, template:`<li>姓名:{{name}},年龄:{{age}}</li>` } //全局注册组件 Vue.component("my-comp",myComp); //创建Vue实例 var app = new Vue({ el:"#app", data:{ title:"用户列表" } }) </script>
1)局部注册就是哪里要用到组件,就在哪里注册
2)局部注册的方式是,在要使用组件的组件或实例中加入一个配置:
//这是另一个要使用my-comp的组件 var otherComp = { components:{ //属性名为组件名称,模板中将使用该名称 //属性值为组件配置对象 "my-comp": myComp }, template : `<div> <!--该组件的其他内容--> <my-comp></my- comp> </div>` }
1、在模板中使用组件特别简单,把组件名当作HTML元素名使用即可。
但要注意以下几点:
1)组件必须有结束。组件可以自结束,也可以用结束标记结束,但必须要有结束,以下两种方式都可以:
<my-comp/> <!-- 或 --> <my-comp></my-comp>
2)组件的命名规范:
无论你使用哪种方式注册组件,组件的命名需要遵循规范。组件可以使用kebab-case短橫线命名法,也可以使用PascalCase大驼峰命名法。下面两种命名均是可以的:
var otherComp = { components:{ "my-comp": myComp,//方式1 "MyComp": myComp//方式2 } }
实际上,使用小驼峰命名法camelCase 也是可以识别的,只不过不符合官方要求的规范。
3)使用PascalCase方式命名还有一个额外的好处,即可以在模板中使用两种组件名:
var otherComp = { components:{ "MyComp": myComp } }
在模板中以下两种方式都可以使用:
<!--可用--> <my-comp /> <MyComp />
4)局部注册代码案例:
<div id="app"></div> <script> //自定义组件 var myComp = { data(){ return { name:"潘子夜", age:20 } }, template:`<li>姓名:{{name}},年龄:{{age}}</li>` } //创建Vue实例 var app = new Vue({ el:"#app", data:{ title:"用户列表" }, components:{ "MyComp":myComp }, template: `<div id="app"> {{title}} <ul> <my-comp></my-comp> <my-comp/> <MyComp></MyComp> <MyComp/> </ul> </div>` }) </script>
vue
针对tr
单独封装的组件不能直接使用组件标签,会将tr放到table外,需要借助is
属性指定组件标签。比如我们定义tr组件如下:
var myComp = { props:["item"], template:`<tr> <td>aaa</td> <td>bbb</td> </tr>` };
引用如下:
<div id="app"> <table> <tr> <td>产品编号</td> <td>产品名称</td> </tr> <my-comp></my-comp> </table> </div>
<div id="app"> <table> <tr> <td>产品编号</td> <td>产品名称</td> </tr> <tr is="my-comp"></tr> </table> </div>
一个组件创建好后,往往会在各种地方使用它。它可能多次出现在vue实例中,也可能出现在其他组件中。于是就形成了一个组件树。
1、大部分组件要完成自身的功能,都需要一些额外的信息,比如一个头像组件,需要告诉它头像的地址,这就需要在使用组件时向组件传递数据,传递数据的方式有很多种,最常见的一种是使用组件属性props
2、首先在组件中申明可以接收哪些属性,例如:
var myComp = { props:["p1", "p2","p3"], //和vue实例一样,使用组件时也会创建组件的实例 //而组件的属性会被提取到组件实例中,因此可以在模板中使用 template: `<div> {{p1}},{{p2}}, {{p3}} </div>` }
3、在使用组件时,向其传递属性:
var otherComp = { components: { "my-comp":myComp } data(){ return { a:1 } }, //可以使用:绑定属性传值,也可以使用属性直接赋值传值 template: `<my-comp :p1="a" :p2="2" p3="3"/>` }
4)注意:在组件中,props
属性中数据是别人传过来的,并且是只读的,绝不可以更改,这叫做单向数据流,从上面的组件流向下面的组件。组件中data
中的数据是可以修改的,因为这里组件自身的数据。
5)案例代码:
<div id="app"> {{title}} <ul> <my-comp v-for="(user,i) in users" :key="i" :name="user.name" :age="user.age"/> </ul> </div> <script> //自定义组件 var myComp = { props:["name","age"], template:`<li>姓名:{{name}},年龄:{{age}}</li>` } //创建Vue实例 var app = new Vue({ el:"#app", data:{ title:"用户列表", users:[ {name:"张三",age:25}, {name:"王五",age:45}, {name:"李四",age:65} ] }, components:{ "my-comp":myComp } }) </script>
如果需要通过点击事件删除上述案例的用户纪录,点击删除是子组件触发的,而数据是在父组件中的,存在单向数据流问题,那么该怎么做?
需要子组件与父组件通信,子组件发布点击事件,父组件订阅子组件的事件,一旦监听到该事件,父组件就删除对应的用户纪录数据,也就是我们的发布订阅模式。
<div id="app"> {{title}} <ul> <my-comp v-for="(user,i) in users" :key="i" :name="user.name" :age="user.age" :index="i" @delete="handleDelete"/> </ul> </div> <script> //自定义组件 var myComp = { //接收index属性值 props:["name","age","index"], template:`<li>姓名:{{name}},年龄:{{age}} <button @click="handleClick">删除</button></li>`, methods:{ handleClick(){ //子组件向外触发delete事件,在父组件中应用时绑定该事件,参数为index,即纪录索引 this.$emit("delete",this.index) } } } //创建Vue实例 var app = new Vue({ el:"#app", data:{ title:"用户列表", users:[ {name:"张三",age:25}, {name:"王五",age:45}, {name:"李四",age:65} ] }, components:{ "my-comp":myComp }, methods:{ handleDelete(index){ this.users.splice(index,1); } } }) </script>