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>













