1、初识Vuejs
1.1、为什么学习Vuejs?
- 可能你的公司正要用Vue将原项目重构
- 可能你的公司新项目决定使用Vue技术栈
- 可能你正在找工作,会发现十个前端八个对Vue有或多或少的要求
- 当然,最重要的是Vue非常火,很流行
1.2、简单认识Vuejs
- Vue(读音/vju:/ ,类似于view)
- Vue是一个渐进式框架
- 渐进式意味着你可以将Vue作为你应用的一部分嵌入其中,带来更丰富的交互体验
- 或者如果你希望更多的业务逻辑使用Vue实现,那么Vue的核心库及其生态系统;
- 比如Core + Vue-router + Vuex,也可以满足你各种各样的需求;
- Vue有很多特点和Web开发中常见的高级功能
- 解耦视图和数据
- 可服用的组件
- 前端路由技术
- 状态管理
- 虚拟DOM
1.3、Vuejs的安装
CDN引入
- 开发环境(包好了有帮助的命令行警告)
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
- 生产环境,优化了尺寸和速度
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
下载和引入
开发环境:https://vuejs.org/js/vue.js 生产环境:https://vuejs.org/js/vue.min.js
npm安装管理
1.4、Vuejs初体验
之前范式:命令式编程 如今范式:声明式编程
Hello Vuejs
<body>
<div id="app">
{{message}}
</div>
</body>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'hello, vue'
}
});
</script>
Vue列表展示
<body>
<div id="app">
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
movies: ['星际穿越', '大话西游', '少年派', '盗梦空间']
}
});
</script>
案例:计数器(v-on 事件监听)
<body>
<div id="app">
<h2>当前计数:{{counter}}</h2>
<button v-on:click="increament()">+</button>
<button v-on:click="subtraction($event)">-</button>
</div>
</body>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
counter: 0
},
methods: {
increament: function (){
this.counter++;
},
subtraction: function (){
this.counter--;
}
}
});
</script>
2、MVVM
2.1、MVVM
View层
- 视图层
- 在前端开发中,通常指DOM
- 主要用来给用户展示信息
Model层
- 数据层
- 可能是固定的死数据,更多是来自服务器、网络上的数据
ViewModel
- 视图模型层
- View与Model层沟通的桥梁
- 实现了Data Binding(数据绑定),将Model的改变实时反映到View中
- 实现了DOM Listener(DOM监听),当监听到DOM发生某种事件时,改变对应的Data
2.2、Vue中的MVVM
2.3、Vue的生命周期
3、插值操作
3.1、Mustache语法:{{}}
<div id="app">
<h2>{{message}}, {{firstName + ' ' + lastName}}</h2>
<h2>{{counter* 2}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 'Hello',
firstName: 'kobe',
lastName: 'bryant',
counter: 100
}
});
</script>
3.2、v-once
被v-once修饰的变量,一旦赋值无法更改
<div id="app">
<h2 v-once>{{counter}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
counter: 100
}
});
</script>
3.3、v-html
将字符串形式的html代码添加到dom中
<div id="app">
<h2 v-html="url"></h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
url: '<a href="http://www.baidu.com">百度</a>'
}
});
</script>
3.4、v-pre
页面会原封不动的展示标签中的内容
<div id="app">
<h2 v-pre>{{url}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
url: '<a href="http://www.baidu.com">百度</a>'
}
});
</script>
3.5、v-cloak
当你的js解析html时产生了停顿,就将显示出未渲染的半成品页面,v-cloak属性在渲染之后会自动删除,可以与display:none结合使用解决这个问题
<style>
[v-cloak]{
display: none;
}
</style>
<div id="app" >
<h2 v-cloak>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
setTimeout(function (){
const app = new Vue({
el: "#app",
data: {
message: '你好'
}
});
}, 2000)
</script>
3.6、v-if / v-else-if / v-else
<div id="app">
<h1 v-if="isShow == true">true</h1>
<h1 v-else>false</h1>
</div>
3.7、v-show
控制元素是否显示 v-show 与 v-if 的区别
v-show 为false 时,只是加了一个display : none 的样式 v-if 为false 时,元素不会被渲染到页面,直接从dom清除 简单来说前者 操作样式 ,而后者 操作dom 树 当切换频率 很高 时,使用v-show 频率较低 ,v-if 更佳
3.8、v-for
<div id="app">
<ul>
<li v-for="(movie, i) in movies">{{movie}}----{{i}}</li>
</ul>
<ul>
<li v-for="(value, key) in person">{{value}}----{{key}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
movies: ['海贼王', '火影忍者', '名侦探柯南'],
person: {
id: 8,
name: '柯南',
profession: 'detective'
}
}
});
</script>
4、v-bind(:)
4.1、动态绑定属性
<div id="app">
<img v-bind:src="imageURL" alt="">
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
imageURL: 'https://img11.360buyimg.com/seckillcms/s280x280_jfs/t1/197347/6/13295/199552/61696bf5E162a9a54/23d76b487b81fe05.jpg.webp'
}
});
</script>
4.2、动态绑定样式
<style>
.red{
color: red;
}
.green{
color: green;
}
</style>
<div id="app">
<h1 class="title":class="getClasses()">你好啊</h1>
<button @click = "red">红色</button>
<button @click = "green">绿色</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isGreen: false,
isRed: false
},
methods: {
red: function (){
this.isRed = !this.isRed;
},
green: function (){
this.isGreen = !this.isGreen;
},
getClasses: function (){
return {red: this.isRed, green: this.isGreen};
}
}
});
</script>
4.3、作业
需求 鼠标移到上边字体变红,移出恢复
<style>
.red {
color: red;
}
</style>
<div id="app">
<ul>
<li v-for="(m, index) in movies" @mouseover="i = index" @mouseleave="i = -1" :class="getClasses(index)">{{m}}</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
movies: ['海贼王', '火影忍者', '名侦探柯南', '蜡笔小新'],
i: -1
},
methods: {
getClasses: function (i) {
if(i == this.i){
return {red: true};
}
}
}
});
</script>
4.4、对象语法和数组语法
<ul>
<li :style="{fontSize: '50px', color: 'red'}">{{message}}</li>
<li :style="[{fontSize: '60px'},{color: 'red'}]">{{message}}</li>
</ul>
4.5、计算属性(computed)
computed: {
fullname: {
set: function (newValue) {},
get: function () {
return this.firstName + ' ' + this.lastName;
}
},
full: function () {
return this.firstName + ' ' + this.lastName;
}
}
methods与computed区别:
计算属性存在缓存,return值未发生改变时只会调用一次
5、修饰符
5.1、stop
阻止其他跟随事件, 也不会跟随其他事件
<div id="app">
<div @click = 'divClick' style="background-color: red">
我是盒子
<button @click.stop = 'btnClick'>我是按钮</button>
</div>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
methods: {
btnClick(){
console.log('按钮被点了!!!!!!!!')
},
divClick(){
console.log('盒子被点了');
}
}
});
</script>
5.2、prevent
推迟事件的发生
<div id="app">
<form action="http://www.baidu.com">
<input type="submit" value="提交" @click.prevent="submitClick">
</form>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
methods: {
submitClick(){
console.log('提交被推迟');
}
}
});
</script>
5.3、enter
监听enter键
<div id="app">
<for m action="http://www.baidu.com">
<input type="text" @keyup="enter">
<input type="text" @keyup.enter="enter">
</form>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
methods: {
enter(){
console.log('回车');
}
}
});
</script>
5.4、once
被修饰元素只有第一次会被渲染
5.5、native
用于本地组件生效
5.6、数组中的响应式
并不是所有改变数组的方式都能做到响应式 只有返回新数组才会发生响应,而仅仅是通过下标改变原数组的值是不能做到响应式的
5.7、JavaScript中的高级函数
filter
fileter是数组的一个高阶函数,参数是一个回调函数;也就是说,他会根据数组的长度来决定回调的次数,返回值是bool值;
当为true 时,会将当前元素放入一个新数组,全部执行完成会将新数组返回; 当为false ,不做任何操作
const nums = [11,1,111,1111];
let newArry = nums.filter(function (n){
return n < 100;
});
console.log(newArry);
map
参数也是回调函数,迭代每个元素,执行某种操作后返回
const nums = [11, 1, 111, 1111];
let newArry = nums.map(function (n) {
return n * 2;
});
console.log(newArry);
reduce
对数组中的元素进行汇总
- reduce函数参数有两个
- 一为回调函数,其中参数(preValue,n)
- preValue:上一次回调函数的返回值
- n:当前下标的元素
- 二为preValue的初始值
- 回调次数为数组长度,当循环完毕返回回调函数的最终值
所有元素相加之和:
const nums = [11, 1, 111, 1111];
let sum = nums.reduce(function (preValue, n) {
return preValue + n;
}, 0);
console.log(sum);
三者结合
常规做法
const nums = [11, 1, 111, 1111];
let sum = nums.filter(function (n) {
return n < 100;
}).map(function (n){
return n * 2;
}).reduce(function (preValue, n) {
return preValue + n;
}, 0);
console.log(sum);
简洁做法
const nums = [11, 1, 111, 1111];
let sum = nums.filter(n => n < 100)
.map(n => n * 2)
.reduce((pre, n) => pre + n);
console.log(sum);
6、v-model
- 表单绑定
- 表单控件在实际开发中很常见,特别是对于用户信息的提交,需要大量表单
- Vue中使用v-model来实现表单元素和数据的双向绑定
6.1、input和data的双向绑定
<div id="app">
<input type="text" v-model="message"/>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: '哈哈哈'
}
});
</script>
v-model其实是个语法糖,他的背后本质包含两个操作:
- v-bind绑定属性value
- v-on执行给当前元素绑定input事件
6.2、radio和data的双向绑定
<div id="app">
<input type="radio" value="男" name="sex" v-model="sex"/> 男
<input type="radio" value="女" name="sex" v-model="sex"/> 女
<h1>您选择的性别是:{{sex}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
sex: '男'
}
});
</script>
6.3、checkbox和data的双向绑定
单个单选框
<div id="app">
<input type="checkbox" v-model="isAgree"/> 同意协议
<button :disabled="!isAgree">下一步</button>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
isAgree: false
}
});
</script>
关于多选框true和false的问题 多个多选框
<div id="app">
<input type="checkbox" value="唱" v-model="hobbies"/> 唱
<input type="checkbox" value="跳" v-model="hobbies"/> 跳
<input type="checkbox" value="rap" v-model="hobbies"/> rap
<input type="checkbox" value="篮球" v-model="hobbies"/> 篮球
<h1>您的爱好是:{{hobbies}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
hobbies: []
}
});
</script>
6.4、select和data的双向绑定
选择单个
<div id="app">
<select v-model="option">
<option type="checkbox" value="唱"> 唱</option>
<option type="checkbox" value="跳"> 跳</option>
<option type="checkbox" value="rap"> rap</option>
<option type="checkbox" value="篮球"> 篮球</option>
</select>
<h1>您的爱好是:{{option}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
option: 'rap'
}
});
</script>
选择多个
<div id="app">
<select v-model="option" multiple>
<option type="checkbox" value="唱"> 唱</option>
<option type="checkbox" value="跳"> 跳</option>
<option type="checkbox" value="rap"> rap</option>
<option type="checkbox" value="篮球"> 篮球</option>
</select>
<h1>您的爱好是:{{option}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
option: []
}
});
</script>
6.5、修饰符
lazy
有时我们更改输入框的数据不想让它实时更新,这时我们就可以使用lazy修饰符,它只有在元素回车失去焦点才会更新
number
<div id="app">
<input v-model.number="message" type="text">
{{message}}
{{typeof(message)}}
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: 0
}
});
</script>
trim
去除首尾空格
7、组件化
- 人面对复杂问题的处理方式:
- 任何一个人处理信息的逻辑能力都是有限的
- 所以,当面对一个非常复杂的问题时,我们不太可能一次性搞定一大堆的内容。
- 但是,我们人有一种天生的能力,就是将问题进行拆解。
- 如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,你会发现大的问题也会迎刃而解。
- 组件化也是类似的思想:
- 如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
- 但如果,我们将一个页面拆分成一个个小的功能块
- 每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了。
7.1、组件的使用(全局与局部)
- 创建
调用Vue.extend() 方法创建组件构造器 - 注册
调用Vue.component() 方法注册组件 - 使用
在Vue作用范围内 使用组件
<div id="app">
<smile></smile>
</div>
<script src="../js/vue.js"></script>
<script>
const myComp = Vue.extend({
template: "<div>\n" +
" <h1>哈</h1>\n" +
" <h2>呵呵</h2>\n" +
" <h3>嘿嘿嘿</h3>\n" +
" </div>"
});
Vue.component('smile', myComp);
const app = new Vue({
el: "#app",
components: {
cpn: smile
}
});
</script>
Vue.extend()
- 调用Vue.extend()创建的是一个组件构造器。
- 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
- 该模板就是在使用到组件的地方,要显示的HTML代码。
- 事实上,这种写法在Vue2g的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
Vue.component
- 调用Vue.component0是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
- 所以需要传递两个参数:
- 注册组件的标签名
- 组件构造器
7.2、组件的使用(语法糖)
==Vue提供了一个语法糖,省去了手动调用extend的步骤,直接在注册时传入对象,component方法会在底层帮你调用。
<div id="app">
<uncst></uncst>
<cst></cst>
</div>
<script src="../js/vue.js"></script>
<script>
Vue.component('cst', {
template: `<div>
<h1>哈</h1>
<h2>全局</h2>
<h3>嘿嘿嘿</h3>
</div>`
});
const app = new Vue({
el: "#app",
components: {
'uncst': {
template: `<div>
<h1>哈</h1>
<h2>全局</h2>
<h3>嘿嘿嘿</h3>
</div>`
}
}
});
</script>
7.3、父子组件
const myCompSon = Vue.extend({
template: `<div>
<h1>哈</h1>
<h2>全局</h2>
<h3>嘿嘿嘿</h3>
</div>`
});
const myCompFather = Vue.extend({
template: `<div>
<h1>哈</h1>
<h2>全局</h2>
<h3>嘿嘿嘿</h3>
</div>`,
components: {
cpn2: myCompSon
}
});
7.4、模板html分离
第一种:template标签
<template id="cpn1">
<h1>第一种模板</h1>
</template>
<script>
Vue.component('cpn1', {
template: '#cpn1'
})
</script>
第二种:script标签
<script type="text/x-template" id="cpn2">
<div>
<h1>第二种模板</h1>
</div>
</script>
<script>
Vue.component('cpn2', {
template: '#cpn2'
})
</script>
7.5、组件中的数据
- 组件不能直接访问实例中的数据
- 可以在组件中添加自己的data属性
- 必须定义为一个函数,且返回一个对象
- 对象中保存着数据
Vue.component('cpn', {
data: function () {
return {
title: '我是标题'
}
},
template: `
<div>
<h1>{{title}}</h1>
</div>
`
});
- 为什么Vue要将data设计成一个函数并return呢?
因为当页面中使用了多个 相同组件时,如果不是 函数,data数据就会共享 ,产生不必要的bug,而使用函数,每次调用都能返回一个新的对象,能够很好的隔离 各个组件 ,因此被设计成函数。
7.6、父子间的通信
父传子:props属性 子传父:emit事件
父传子
- 方式一:字符串数组,数组中的字符串就是传递时的名称
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
注:若想在props中使用驼峰命名法,子标签的v-bind绑定的属性必须用’-‘分割 比如:
props:cMessage v:bind:c-message = ‘message’ v-bind作用是使父组件变量名生效,当然你也可以选择不用,直接将值放进去,虽效率不高,然易于理解
<div id="app">
<cpn :cmovies="movies" :cmessage="messages"></cpn>
</div>
<script src="../js/vue.js"></script>
<script type="text/x-template" id="cpn_tpe">
<div>
<h1>{{cmessage}}</h1>
<h1>{{cmovies}}</h1>
</div>
</script>
<script>
const cpn = {
template: '#cpn_tpe',
props: {
cmovies: {
type: Array,
default(){
return []
},
required: true
},
cmessage: {
type: String,
default: '默认值'
}
}
}
const app = new Vue({
el: "#app",
data: {
messages: '雷猴啊',
movies: ['海王', '海贼王', '海尔兄弟', '海绵宝宝']
},
components: {
cpn
}
});
</script>
子传父
<div id="app">
<son @item_click="cpn_click"></son>
</div>
<script src="../js/vue.js"></script>
<script type="text/x-template" id="sontemplate">
<div>
<button type="button" v-for="(item) in catagories" @click="btn_click(item)"> <!--1. 检测到点击,调用btn_click-->
{{item.name}}
</button>
</div>
</script>
<script>
const son = {
template: '#sontemplate',
data(){
return {
catagories: [
{id: 'a', name: '热门推荐'},
{id: 'b', name: '手机数码'},
{id: 'c', name: '家用家电'},
{id: 'd', name: '电脑办公'}
]
}
},
methods:{
btn_click(item){
this.$emit('item_click', item);
}
}
}
const app = new Vue({
el: "#app",
components: {
son
},
methods: {
cpn_click(item){
console.log(item.name);
}
}
});
</script>
父子互传案例
<div id="app">
<cpn :cpn_num1="num1" :cpn_num2="num2" @change_num1="changeNum1" @change_num2="changeNum2"></cpn>
</div>
<template id="cpn">
<div>
<h1> props1: {{cpn_num1}}</h1>
<h1> data1: {{dnum1}}</h1>
<input :value="dnum1" @input="inputNum1"/>
<h1> props2: {{cpn_num2}}</h1>
<h1> data2: {{dnum2}}</h1>
<input :value="dnum2" @input="inputNum2"/>
</div>
</template>
<script>
const cpn = {
template: '#cpn',
data(){
return {
dnum1: this.cpn_num1,
dnum2: this.cpn_num2
}
},
props: {
cpn_num1: Number,
cpn_num2: Number
},
methods: {
inputNum1(event){
this.dnum1 = event.target.value;
this.$emit('change_num1', this.dnum1);
this.dnum2 = this.dnum1 * 100;
this.$emit('change_num2', this.dnum2);
},
inputNum2(event){
this.dnum2 = event.target.value;
this.$emit('change_num2', this.dnum2);
this.dnum1 = this.dnum2 * 1/100
this.$emit('change_num1', this.dnum1);
}
}
}
const app = new Vue({
el: "#app",
data: {
num1: 1,
num2: 0
},
components: {
cpn
},
methods: {
changeNum1(value){
this.num1 = value*1;
},
changeNum2(value){
this.num2 = parseInt(value);
}
}
});
</script>
父子访问
8、插槽Slot
8.1、什么是插槽?
- 组件里的插槽,类似于Java抽象类中的抽象方法;
- 因为具有不确定性,因此能够很好的复用;
- 导航栏为一种使用场景
8.1、为什么使用Slot?
Slot翻译为插槽:
- 在生活中很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。
- 插槽的目的是让我们原来的设备具备更多的扩展性。
- 比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等。组件的插槽:
- 组件的插槽也是为了让我们封装的组件更加具有扩展性。
让使用者可以决定组件内部的一些内容到底展示什么。
8.3、案例:单插槽
<div id="app">
<cpn></cpn>
<cpn>
<font>slot</font>
</cpn>
<cpn>!</cpn>
</div>
<template id="cpn">
<slot>
<button>hello,</button>
</slot>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn'
}
const app = new Vue({
el: "#app",
components: {
cpn
}
});
</script>
8.4、案例:具名(多)插槽
<div id="app">
<cpn>
<h1 slot="middle">嘿嘿嘿</h1>
<h2 slot="middle">吼吼吼</h2>
</cpn>
<cpn>
<h1>哈哈哈</h1>
</cpn>
</div>
<template id="cpn">
<div>
<slot name="left">左</slot>
<slot name="middle">中</slot>
<slot name="right">右</slot>
<slot>右右</slot>
<slot>右右右</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn'
}
const app = new Vue({
el: "#app",
components: {
cpn
}
});
</script>
8.5、作用域插槽
- 父组件替换插槽的标签,但数据是由子组件提供
- 比如有些子组件内容要水平展示,或者竖直展示,它的结构是变化的,因此就有了作用域插槽
<div id="app">
<cpn></cpn>
<cpn>
<template slot-scope="aaa">
<span>{{aaa.xxx.join(' - ')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :xxx="pLanguage">
<li v-for="item in pLanguage">{{item}}</li>
</slot>
</div>
</template>
<script>
const cpn = {
template: '#cpn',
data() {
return {
pLanguage: ['Java', 'Python', 'JavaScript', 'MySQL', 'C#']
}
}
}
const app = new Vue({
el: "#app",
data: {
},
components: {
cpn
}
});
</script>
9、模块化
9.1、为什么要有模块化?
命名冲突
比如小明写了a.js ,定义变量a,为1; 小红写了b.js 文件,也定义了a,为2; 如果将a与b同时引入,大概率出现严重问题!
如果将代码改成这样
;(function(){
})()
闭包可以解决这种冲突,但会产生新的问题,js代码无法被引用,复用性太差。 可以将可能被访问的数据返回到一个变量用来保存
var moduleA = (function(){
var result = {}
return result
})()
9.2、ES6的模块化
aaa.js
let a = 1
function sum(num1, num2) {
return num1 + num2
}
export {
a, sum
}
export var a = 1000
export function sum(num1, num2) {
return num1 + num2
}
export default var address = 1
export default function(){
console.log("匿名函数");
}
bbb.js
import {a, sum, addr} from './aaa.js'
console.log('a = ' + a)
console.log(sum(1, 2))
import * as aaa from './aaa.js'
console.log(aaa.变量名);
ccc.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ES6模块化</title>
<script src="../js/bbb.js" type="module"/>
</head>
</html>
10、webpack
10.1、认识webpack
- 从本质来讲,webpack是一个现代的JS应用的
静态模块打包 工具。 - 有些文件,如.sass,ES6语法,要转成ES5大部分浏览器才能支持。所以要通过一些工具做一些打包转化之类的工作。
- webpack不仅用来
打包 ,还能进行模块化 ,帮我们处理模块之间的依赖关系 打包 就是将各种资源合并成一或多个包(Bundle)
10.2、webpack的起步
- webpack依赖node环境
node 必须包含各种依赖的包 才能正常执行代码,- 所以安装node时会自动安装帮助我们
管理各种包 的工具npm (node package manager)
10.3、webpack的配置
- 安装支持环境nodejs,会自动安装npm
- 全局安装webpack
npm install webpack@3.6.0 -g
- 局部安装webpack(后需用到)
cd 对应目录
npm install webpack@3.6.0 --save-dev
10.4、案例
- 编写mainUtils.js
function sum(num1, num2){
return num1 + num2
}
function mul(num1, num2) {
return num1 * num2
}
module.exports = {
sum, mul
}
export const id = 1
export const name = 'Candy'
export const age = 18
- 编写info.js
export const id = 1
export const name = 'Candy'
export const age = 18
- 编写main.js
const {sum, mul} = require('./mainUtil.js')
console.log(sum(1, 2));;
console.log(mul(1, 4));;
import {id, name, age} from './mainUtil'
console.log(id);
console.log(name);
console.log(age);
- 终端打包
PS E:\VueProjects\Day-01> webpack ./js/main.js ./dist/bundle.js
Hash: 0237d7838c5fb13a1c57
Version: webpack 3.6.0
Time: 59ms
Asset Size Chunks Chunk Names
bundle.js 2.76 kB 0 [emitted] main
[0] ./js/main.js 69 bytes {0} [built]
[1] ./js/mainUtil.js 142 bytes {0} [built]
PS E:\VueProjects\Day-01>
会将main.js及所有依赖js打包成一个bundle.js文件 - 编写main.html引入bundle.js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../dist/bundle.js"></script>
</head>
</html>
结果展示:
10.5、配置文件设置出入口
- 终端输入
npm init package name 起个名字,之后可以一路回车 生成package.json 文件 如果使用了node依赖的包 ,需要这个文件 - 项目根目录编写
webpack.config.js const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(_dirname, 'dist'),
filename: 'bundle.js'
}
}
- 打包:
终端输入webpack 报错: ERROR in Entry module not found: Error: Can't resolve 'src/main.js' in 'E:\VueProjects\Day-01 检查entry路径 果然是路径错了 继续打包,成功!
10.6、打包需要注意的问题:
- 有时候使用了比如`4.0版本的语法
全局 的打包工具是3.6 的,就会出问题- 这时候需要
配置本地 webpack - 在项目目录下输入
npm install webpack@4.0.0 --save-dev 来下载本地webpack - 生成本地文件
终端 使用webpack都会使用全局 的webpack,不可选package.json 的script 关键字中设置build ,值为webpack,终端执行npm run build ,会优先使用本地 webpack
10.7、loader
- Webpack 本身只能处理 JS 模块,若要处理其他类型的文件,就需要扩展 loader
.css 的处理
- 如果需要使用 css 文件,就需要使用到
css-loader 和 style-loader , - css-loader 解析CSS文件,使用import加载并返回CSS代码
- style-loader 将模块的导出作为样式添加到DOM
使用步骤:
-
安装 通过npm安装 需要使用的loader,全局安装需要参数 -g npm install css-loader@2.0.2 style-loader@0.23.1 --save-dev
执行以上命令会在当前目录生成 node_modules 目录,它是 css-loader 和 style-loader 的安装目录。 -
配置 在webpack.config.js中的modules 关键字下进行配置 大部分loader module: {
loaders: [{
test: /\.css$/,
loader: "style-loader!css-loader"
}]
}
编写style.css文件,在入口文件引用 require("./css/style.css");
-
打包运行 成功!
.less 的处理
- 编写my.less
@fontSize: 50px;
@fontColor: black;
body{
font-size: @fontSize;
color: @fontColor;
}
- 在入口文件中引用
require("./css/my.less");
- 本地安装less的loader
npm install less-loader@4.1.0 less@3.9.0 --save-dev
安装成功 - 在webpack.config.js中使用loader
{
test: /\.less$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader"
},
{
loader: "less-loader"
}
]
}
- 打包运行
npm run build
静态文件 的处理
- 安装依赖loader
npm install url-loader@1.1.2 --save-dev
- 在webpack.config.js中使用loader
{
test: /\.png|gif|jpg|jpeg$/,
use: [
{
loader: "url-loader"
options: {
limit: 8192
}
}
]
}
- file-loader
当加载的图片,小于limit时,会将图片编译成base64字符串形式.npm install file-loader@3.0.1 --save-dev
使用file-loader需要注意的第一个问题
ES6 的处理
- 如果你仔细阅读webpack打包的js文件
- 发现写的ES6语法并没有转成ES5
- 那么就意味着可能一些对ES6还不支持的浏览器不能很好的运行我们的代码。
- 此时我们需要手动进行处理
- 安装babel-loader
npm install babel-loader@7 babel-core babel-preset-es2015 --save-dev
- 配置
webpack.config.js -> module.exports -> module -> rule{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: "babel-loader",
options: {
presets: ['es2015']
}
}
]
}
- 打包
npm run build
成功!
10.8、Webpack的Vue
- npm安装vue
npm install vue@2.5.21 --save
- 配置
出口文件中配置
import Vue from 'vue'
const app = new Vue({
el: '#app',
data: {
message: '信息'
}
})
html页面body中配置 <div id="app">
{{message}}
</div>
- 运行报错
npm run build
vue实例中的template
- 如果vue实例中有
template 属性,会将该属性值进行编译,将编译后的虚拟dom直接替换 掉vue实例绑定的元素(即el 绑定的那个元素); - template属性中的dom结构
只能有一个 根元素 - 如果有多个根元素需要使用v-if、v-else、v-else-if设置成只显示其中一个根元素;
- vue -> app.js
export default {
template: `
<div>
<h1>{{name}}</h1>
</div>
`,
data(){
return {
name: 'Candy'
}
},
methods: {
btnClick(){
console.log("我是组件!");
}
}
}
- 在出口文件中配置
import Vue from 'vue'
import app from './vue/app'
new Vue({
el: '#app',
template: '<app/>',
components: {
app
}
})
- 运行
- 升级写法 -> 模板分离
- 创建Vue组件
- 将之前模板移入
- script移入
- 样式移入
- 出口函数中更改配置
import app from './vue/app'
- 安装loader
npm install vue-loader vue-template-compiler --save-dev
- 引入子组件
vue -> Cpn.vue<template>
<div>
<h1>我是子组件</h1>
</div>
</template>
<script>
export default {
name: "Cpn"
}
</script>
<style scoped>
</style>
在vue -> App.vue导入,components中注册import Cpn from './Cpn.vue'
如果不像每次都写后缀,可以在配置文件的resolve关键字中加入以下代码:extensions: ['.js', '.css', '.vue']
10.9、plugin的使用
- plugin是插件的意思,通常用于对现有的架构进行扩展
- loader主要用于转换类型,相当于转换器;
plugin是对webpack本身的扩展,是一个扩展程序
版权声明
- 配置文件中引入webpack
const webpack = require('webpack')
- module.exports中添加plugins关键字
plugins: [
new webpack.BannerPlugin('最终版权归Monster所有')
]
- 成功
打包html
- 安装插件
npm install html-webpack-plugin@3.2.0 --save-dev
- 配置引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
...
new HtmlWebpackPlugin()
- 打包运行
- 在dist下生成index.html,帮我们引入了bundle.js
- 之前配置的拼接路径
dist/ 不再需要 - 但细细一看,我们id为app的div不见了,这是因为此页面是自动生成的,我们可以在插件配置中添加原页面为模板
重新打包
压缩
- 安装
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
- 引入
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
...
new UglifyjsWebpackPlugin ()
- 丑化
10.10、搭建本地服务器
- webpack提供了一个可选的本地开发服务器
- 基于node.js搭建
- 内部使用express框架
- 可以实现让浏览器自动刷新显示修改后的结果。
- 每次修改时会将结果放到内存,等我们手动发布后才会放到磁盘
不过他是一个单独的模块,需要先进行安装
npm install webpack-dev-server@2.9.3 --save-dev
启动服务器的方式:
- 相对路径
.\node_modules\.bin\webpack_dev_server
- 脚本启动
在package.json中的scripts关键字中配置"dev": "webpack-dev-server --open"
npm install dev
10.11、配置分离
有些开发时的配置等项目上线后就没用了 我们可以将他们抽离出来
- 项目根目录创建build文件夹
- 在build下创建base.config.js,里面放一些开发上线通用的配置
- 创建prod.config.js,放生产时的配置
- 创建dev.config.js,放开发时的配置
- 为了最终能合并配置文件,需要安装webpack-merge
npm isntall webpack-merge --save-dev
- 在prod.config.js和dev.config.js中引入
const baseConfig = require('./base.config')
const WebpackMerge = require('webpack-merge')
module.exports = WebpackMerge(baseConfig, {
})
- 项目启动时会默认启动webpack.config.js,所以我们需要对我们的配置文件进行单独配置
- 更改打包位置
通过观察可以发现打包位置在build下,是因为我们之前设置的是配置文件当前目录下打包,我们可以更改一下,找到base.config.js,更改output -> path的第二个参数为../dist
项目地址: https://gitee.com/candy-xiaojing/vue-study.git
11、Vue CLI
11.1、简介
- 如果你只是简单写几个Vue的Demo程序,那么你不需要Vue CL.
- 如果你在开发大型项目,那么你需要,并且必然需要使用Vue CLI
- 使用Vuejs开发大型应用时,我们需要考虑代码目录结构、项目结构和部署、热加载、代码单元测试等事情。
- 如果每个项目都要手动完成这些工作,那无疑效率比较低效,所以通常我们会使用一些脚手架工具来帮助完成这些事情。
CLI是什么意思? CLI是Command-Line Interface,翻译为命令行界面,但是俗称脚手架. Vue CLI是一个官方发布vuejs项目脚手架 使用vue-cli可以快速搭建Vue开发环境以及对应的webpack配置.
11.2、安装
安装NodeJs
- 可以直接在官方网站中下载安装.
- 网址: http://nodejs.cn/download/
检测安装的版本
- 默认情况下自动安装Node和NPM
- Node环境要求8.9以上或者更高版本
更换淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装脚手架
cnpm install -g @vue/cli
这里安装的是CLI3,Vue CLI2和3使用了相同的vue命令,因此被覆盖了,如果还想使用旧版,可以安装一个桥接工具
cnpm install @vue/cli-init -g
11.3、Vue CLI2初始化项目
初始化参数解读
vue init webpack my_project
初始化参数:
Project name : 项目名称初始化命令中的名称是最终生成文件夹的名称,这里才是真正的项目名,一般情况下保持一致,直接回车Project description :描述信息,自定义,回车Author :作者信息,自定义,回车Runtime Compiler / Runtime Only :初学者可以选择前者,回车vue-router :是否安装路由,自定义Yes / No,回车Use ESLint to lint your code?(Y/n) :是否对你的代码开启严格模式,自定义,回车 6.1. Pick an ESLint preset(Standard / Airbnb / none):选择规范模板,回车- Set up unit tests?(Y/n)是否需要单元测试,自定义,回车
- Setup e2e tests with NightWatch:end to end 安装NightWatch,是一个利用selenium或webdriver或phantomjs等进行自动化测试的框架,自定义
- NPM / Yarn:使用什么工具管理我们的项目,自定义,回车
环境解读
node由C++开发,内嵌了V8引擎,可以直接通过命令运行js代码
node xxx.js
11.4、Vue CLI3初始化项目
vue create 项目名
|