介绍
Vue.js是一套构建用户界面的框架,只关注视图层。可以减少不必要的DOM操作,提高渲染效率。
打包工具:WebPack和Gulp。
在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑。
框架:一套完整的技术解决方案,对项目侵入性较大;库(插件):提供某一小功能,侵入性小。
mvvm
MVVM是前端视图层的概念,主要关注于视图层分离,把前端的视图层分为三部分,Model(数据)、View(每个页面的HTML结构)、ViewModel(调度者,数据双向绑定,可以理解为View和Model的通信桥梁)。
引入
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<!-- Vue 实例所控制的这个元素区域,就是我们的V -->
<div id="vue_det">
<h1>url : {{url}}</h1>
<h1>{{details()}}</h1>
</div>
<script type="text/javascript">
// new 出来的这个 vm 对象,就是我们 MVVM中的 VM调度者
var vm = new Vue({
// 表示new 的这个 Vue 实例,要控制页面上的哪个区域
el: '#vue_det',
// 这里的 data 就是 MVVM中的 M,专门用来保存 每个页面的数据的
data: {
url: "http://www.baidu.com"
},
methods: {
details: function() {
return this.url + " - 百度一下";
}
}
})
document.write(vm.url)
</script>
</body>
</html>
文本插值使用双大括号: {{ }}
Vue调试
谷歌应用商店搜索Vue.js devtools
教程
https://cn.vuejs.org/v2/guide/
https://github.com/pagekit/vue-resource
http://cubic-bezier.com/#.17,.67,.83,.67
指令
指令是带有 v-
前缀的特殊属性。指令用于在表达式的值改变时,将某些行为应用到 DOM
上。
v-cloak
解决插值表达式闪烁问题,只替换占位符。
v-text
v-text默认没有闪烁问题,替换内容时会把整个元素替换。
v-html
用于输出html格式的,也会替换全部内容。
v-bind
绑定属性的指令,用于监听DOM事件。v-bind:
可以省略为:
。变量加常量的效果可以实现。
v-on
事件绑定机制。v-on:
可以省略为@
。
代码:
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<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>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<!-- 使用 v-cloak 能够解决 插值表达式闪烁的问题 -->
<p v-cloak>++++++++ {{ msg }} ----------</p>
<!-- 默认 v-text 是没有闪烁问题的 -->
<!-- v-text会覆盖元素中原本的内容,但是 插值表达式 只会替换自己的这个占位符,不会把 整个元素的内容清空 -->
<h4 v-text="msg">==================</h4>
<div>{{msg2}}</div>
<div v-text="msg2"></div>
<div v-html="msg2">1212112</div>
<!-- v-bind: 是 Vue中,提供的用于绑定属性的指令 -->
<input type="button" value="按钮" v-bind:title="mytitle + '123'">
<!-- 注意: v-bind: 指令可以被简写为 :要绑定的属性 -->
<!-- v-bind 中,可以写合法的JS表达式 -->
<!-- Vue 中提供了 v-on: 事件绑定机制 -->
<input type="button" value="按钮" @click="show">
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '123',
msg2: '<h1>哈哈,我是一个大大的H1, 我大,我骄傲</h1>',
mytitle: '这是一个自己定义的title'
},
methods: { // 这个 methods属性中定义了当前Vue实例所有可用的方法
show: function () {
alert('Hello')
}
}
})
</script>
</body>
</html>
示例之跑马灯
<!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>Document</title>
<!-- 导入Vue包 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="浪起来" @click="lang">
<input type="button" value="低调" @click="stop">
<h4>{{ msg }}</h4>
</div>
<script>
// 注意:在 VM实例中,如果想要获取 data 上的数据,或者 想要调用 methods 中的 方法,必须通过 this.数据属性名 或 this.方法名 来进行访问,这里的this,就表示 我们 new 出来的 VM 实例对象
var vm = new Vue({
el: '#app',
data: {
msg: '猥琐发育,别浪~~!',
intervalId: null // 在data上定义 定时器Id
},
methods: {
lang() {
if (this.intervalId != null) return;
this.intervalId = setInterval(() => {
var start = this.msg.substring(0, 1)
// 获取到 后面的所有字符
var end = this.msg.substring(1)
// 重新拼接得到新的字符串,并赋值给 this.msg
this.msg = end + start
}, 400)
// 注意: VM实例,会监听自己身上 data 中所有数据的改变,只要数据一发生变化,就会自动把 最新的数据,从data 上同步到页面中去;【好处:程序员只需要关心数据,不需要考虑如何重新渲染DOM页面】
},
stop() { // 停止定时器
clearInterval(this.intervalId)
// 每当清除了定时器之后,需要重新把 intervalId 置为 null
this.intervalId = null;
}
}
})
</script>
</body>
</html>
v-model
双向数据绑定,唯一的一个。v-bind
只能实现数据的单向绑定,从M自动绑定到V,无法实现数据的双向绑定。 v-model
可以实现表单元素和Model中数据双向绑定。
注意:只能用到在input、select、text、checkbox、radio
等表单元素中。根据表单上的值,自动更新绑定的元素的值。
v-model
实现简单计算器
<!DOCTYPE html>
<html lang="en" xmlns:v-model="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title></title>
<script src="lib/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="n1"/>
<select v-model="opt">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" v-model="n2"/>
<input type="button" value="=" @click="calc"/>
<input type="text" v-model="result"/>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
n1:0,
n2:0,
result:0,
opt:"+"
},
methods:{
calc:function(){
/*switch (this.opt){
case "+":
this.result = parseInt(this.n1)+parseInt(this.n2)
break;
case "-":
this.result = parseInt(this.n1)-parseInt(this.n2)
break;
case "*":
this.result = parseInt(this.n1)*parseInt(this.n2)
break;
case "/":
this.result = parseInt(this.n1)/parseInt(this.n2)
break;
}*/
var codeStr ="parseInt(this.n1)"+ this.opt + "parseInt(this.n2)"
this.result = eval(codeStr)
}
}
})
</script>
</body>
</html>
v-for
v-for 指令需要以site in sites
形式的特殊语法, sites是源数据数组并且site是数组元素迭代的别名。
循环普通数组;循环对象数组;循环对象,包含key,value,和索引;迭代数字,从1开始。
<div id="app">
<!--循环普通数组-->
<p v-for="item in list">{{item}}</p>
<p v-for="(item,i) in list">索引值是{{i}},每一项是{{item}}</p>
<!--循环对象数组-->
<p v-for="item in list2">{{item.id}}和{{item.name}}</p>
<p v-for="(item,i) in list2">索引值是{{i}}</p>
<!--循环对象-->
<p v-for="val in user">{{val}}</p>
<p v-for="(val,key) in user">{{val}}---{{key}}</p>
<p v-for="(val,key,i) in user">{{val}}---{{key}}----{{i}}</p>
<!--迭代数字-->
<p v-for="count in 10">这是第{{count}}次</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
list:[1,2,3,4,5,6],
list2:[
{id:1,name:"a"},
{id:2,name:"b"},
{id:3,name:"c"},
{id:4,name:"d"}
],
user:{
id:1,
name:"张三",
sex:"男"
}
}
})
</script>
注意:在2.2.0+的版本里,当在组件中使用v-for
时,key是必须的。key属性只能使用number或者string,必须使用v-bind属性绑定的形式,指定key的值。
<p v-for="item in list" :key="item.id">
</p>
v-if
和v-show
根据表达式的值来决定是否插入元素。
v-if
每次都会重新删除或者创建元素,切换消耗性能;v-show
只是切换了元素display:none
样式,较高的初始渲染消耗。
<div id="app">
<input type="button" @click="flag=!flag" value="toggle"/>
<h3 v-if="flag">这是v-if控制</h3>
<h3 v-show="flag">这是v-show控制</h3>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
flag:true
}
})
</script>
v-else-if
和v-else
的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else>
Not A/B
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
type: 'C'
}
})
</script>
</body>
</html>
事件修饰符
修饰符是以半角句号.
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
.stop
阻止事件冒泡
.prevent
阻止默认事件
.capture
捕获事件
.self
只当事件在该元素本身触发时,触发回调。只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡行为。
.once
事件只触发一次
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="lib/vue.js"></script>
<style>
.inner{
height: 150px;
background-color: darkcyan;
}
</style>
</head>
<body>
<div id="app">
<div class="inner" @click="div1">
<input type="button" value="戳他" @click.stop="btn"/>
</div>
<a href="http://www.baidu.com" @click.prevent="linkClick">百度</a>
<div class="inner" @click.capture="div1">
<input type="button" value="戳他" @click="btn"/>
</div>
<div class="inner" @click.self="div1">
<input type="button" value="戳他" @click="btn"/>
</div>
<a href="http://www.baidu.com" @click.prevent.once="linkClick">百度</a>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
div1:function(){
console.log("div1click")
},
btn:function(){
console.log("btnclick")
},
linkClick:function(){
console.log("btnclick")
}
}
})
</script>
</body>
</html>
样式
class样式
格式:
<div id="app">
<!--直接绑定-->
<h1 v-bind:class="['类名1','类名2','类名3']"></h1>
<!--直接绑定,简写-->
<h1 :class="['类名1','类名2','类名3']"></h1>
<!--三元运算-->
<h1 :class="['类名1','类名2',flag?'类名3':'']"></h1>
<!--嵌套对象-->
<h1 :class="['类名1','类名2',{'类名3':flag}]"></h1>
<!--使用对象-->
<h1 :class="{类名1:true,类名2:true,类名3:true}"></h1>
<!--抽取对象-->
<h1 :class="classObj"></h1>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
flag: true,
classObj:{类名1:true,类名2:true,类名3:true}
}
})
</script>
代码:
<!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>Document</title>
<script src="./lib/vue-2.4.0.js"></script>
<style>
.red {
color: red;
}
.thin {
font-weight: 200;
}
.italic {
font-style: italic;
}
.active {
letter-spacing: 0.5em;
}
</style>
</head>
<body>
<div id="app">
<!-- <h1 class="red thin">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
<!-- 第一种使用方式,直接传递一个数组,注意: 这里的 class 需要使用 v-bind 做数据绑定 -->
<!-- <h1 :class="['thin', 'italic']">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
<!-- 在数组中使用三元表达式 -->
<!-- <h1 :class="['thin', 'italic', flag?'active':'']">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
<!-- 在数组中使用 对象来代替三元表达式,提高代码的可读性 -->
<!-- <h1 :class="['thin', 'italic', {'active':flag} ]">这是一个很大很大的H1,大到你无法想象!!!</h1> -->
<!-- 在为 class 使用 v-bind 绑定 对象的时候,对象的属性是类名,由于 对象的属性可带引号,也可不带引号,所以 这里我没写引号; 属性的值 是一个标识符 -->
<h1 :class="classObj">这是一个很大很大的H1,大到你无法想象!!!</h1>
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
flag: true,
classObj: { red: true, thin: true, italic: false, active: false }
},
methods: {}
});
</script>
</body>
</html>
内联样式
<div id="app">
<!--直接绑定-->
<h1 :style="{color:'red','font-weight':200}">哈哈哈</h1>
<!--data-->
<h1 :style="styleObj1">哈哈哈</h1>
<!--多个data-->
<h1 :style="[styleObj1,styleObj2]">哈哈哈</h1>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
styleObj1 : {color:'red','font-weight':200},
styleObj2 : {'font-style':'italic'},
}
})
</script>
过滤器
Vue允许你自定义过滤器,被用作一些常见的文本格式化。过滤器可以用在两个地方:双括号插值和v-bind
表达式,过滤器应该被添加在js表达式的尾部,由管道符(|
)提示。
过滤器中function第一个参数已经被规定死了:永远是管道符前面传递过来的数据。
全局过滤器和私有过滤器:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{ message | data1 }}</p>
<p>{{ message | data2("嘻嘻")}}</p>
<p>{{ message | data1 | data3}}</p>
<p>{{ message | data4 }}</p>
</div>
<script>
//方式一:全局过滤器
Vue.filter('data1',function(value){
return value+"嘻嘻"
})
//方式二:可以多个参数
Vue.filter('data2',function(value,arg1){
return value+arg1
})
//方式三:可以多次调用
Vue.filter('data3',function(value){
return value+"哈哈"
})
new Vue({
el: '#app',
data: {
message: '哈哈哈'
},
//方式四:私有过滤器
filters: {
data4: function (value) {
return value+"嘻嘻"
}
}
})
</script>
</body>
</html>
注意:使用ES6
中的字符串新方法 String.prototype.padStart(maxLength, fillString='')
或 String.prototype.padEnd(maxLength, fillString='')
来填充字符串。
自定义键盘修饰符
Vue.config.keyCodes.f2 = 113
自定义指令
和js相关的最好在inserted
中执行;和样式相关的,一般在bind
中执行。
Vue.directive
:
Vue.directive('focus', {
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
},
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
},
updated: function (el) { // 当VNode更新的时候,会执行 updated, 可能会触发多次
}
})
全局指令和私有指令。自定义指令下的钩子函数。
https://cn.vuejs.org/v2/guide/custom-directive.html
私有指令:
var vm2 = new Vue({
el: '#app',
data: {
},
methods: {},
filters: { // 定义私有过滤器 过滤器有两个 条件 【过滤器名称 和 处理函数】
},
directives: { // 自定义私有指令
}
})
简写方式:
directives: { // 自定义私有指令
'fontsize': function (el, binding) { // 注意:这个 function 等同于 把 代码写到了 bind 和 update 中去
el.style.fontSize = parseInt(binding.value) + 'px'
}
}
vue实例的生命周期
生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期。生命周期钩子 = 生命周期函数 = 生命周期事件。
主要的生命周期函数分类:
- 创建期间的生命周期函数:
- beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好data和methods属性。
- created:实例已经在内存中创建好,此时data和methods已经创建好,此时还没有开始编译模板
- beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
- mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
- 运行期间的生命周期函数:当data改变时候才会执行
- beforeUpdate:状态更新之前执行此函数, 此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为此时还没有开始重新渲染DOM节点
- updated:实例更新完毕之后调用此函数,此时data中的状态值和界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
- 销毁期间的生命周期函数:
- beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
- destroyed:Vue实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
代码:
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
msg: 'ok'
},
methods: {
show() {
console.log('执行了show方法')
}
},
beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
// 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
},
created() { // 这是遇到的第二个生命周期函数
// 在 created 中,data 和 methods 都已经被初始化好了!
// 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
},
beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
// console.log(document.getElementById('h3').innerText)//{{msg}}
// 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
},
mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
// console.log(document.getElementById('h3').innerText)
// 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
},
// 接下来的是运行中的两个事件
beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗? 数据肯定被更新了】
/* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg) */
// 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
},
updated() {
console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
console.log('data 中的 msg 数据是:' + this.msg)
// updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
}
});
页面初始化可以在created中请求网络。
vue-resource实现网络请求
https://github.com/pagekit/vue-resource
直接在页面中,通过script标签,引入vue-resource的脚本文件,注意:引用的先后顺序是:先引用Vue的脚本文件,再引用vue-resource的脚本文件;
<script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.1"></script>
发送get请求:
getInfo() { // get 方式获取数据
this.$http.get('url').then(res => {
console.log(res.body);
})
}
发送post请求:
postInfo() {
// post 方法接收三个参数:
// 参数1: 要请求的URL地址
// 参数2: 要发送的数据对象
// 参数3: 指定post提交的编码类型为 application/x-www-form-urlencoded
this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => {
console.log(res.body);
});
}
发送JSONP请求获取数据:
jsonpInfo() { // JSONP形式从服务器获取数据
this.$http.jsonp(url).then(res => {
console.log(res.body);
});
}
JSONP的实现原理
- 由于浏览器的安全性限制,不允许AJAX访问 协议不同、域名不同、端口号不同的数据接口,浏览器认为这种访问不安全;
- 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓,JSONP只支持Get请求);
- 具体实现过程:
- 先在客户端定义一个回调方法,预定义对数据的操作;- 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口;
- 服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行;
- 客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了;
除了vue-resource之外,axios也可以实现数据请求。
全局配置
// 如果我们通过全局配置了,请求的数据接口 根域名,则 ,在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 / ,否则 不会启用根路径做拼接;
Vue.http.options.root = 'http://vue.studyit.io/';
// 全局启用 emulateJSON 选项
Vue.http.options.emulateJSON = true;
练习-品牌案例
需要bootstra样式
<!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>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link rel="stylesheet" href="./lib/bootstrap-3.3.7.css">
</head>
<body>
<div id="app">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">添加品牌</h3>
</div>
<div class="panel-body form-inline">
<label>
Id:
<input type="text" class="form-control" v-model="id">
</label>
<label>
Name:
<input type="text" class="form-control" v-model="name" @keyup.f2="add">
</label>
<!-- 在Vue中,使用事件绑定机制,为元素指定处理函数的时候,如果加了小括号,就可以给函数传参了 -->
<input type="button" value="添加" class="btn btn-primary" @click="add()">
<label>
搜索名称关键字:
<input type="text" class="form-control" v-model="keywords" id="search" v-focus v-color="'green'">
</label>
</div>
</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Ctime</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr v-for="item in search(keywords)" :key="item.id">
<td>{{ item.id }}</td>
<td v-text="item.name"></td>
<td>{{ item.ctime | dateFormat() }}</td>
<td>
<a href="" @click.prevent="del(item.id)">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<script>
Vue.filter('dateFormat', function (dateStr, pattern = "") {
// 根据给定的时间字符串,得到特定的时间
var dt = new Date(dateStr)
// yyyy-mm-dd
var y = dt.getFullYear()
var m = dt.getMonth() + 1
var d = dt.getDate()
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`
} else {
var hh = dt.getHours()
var mm = dt.getMinutes()
var ss = dt.getSeconds()
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
})
// 自定义全局按键修饰符
Vue.config.keyCodes.f2 = 113
// 使用 Vue.directive() 定义全局的指令 v-focus
// 其中:参数1 : 指令的名称,注意,在定义的时候,指令的名称前面,不需要加 v- 前缀,
// 但是: 在调用的时候,必须 在指令名称前 加上 v- 前缀来进行调用
// 参数2: 是一个对象,这个对象身上,有一些指令相关的函数,这些函数可以在特定的阶段,执行相关的操作
Vue.directive('focus', {
bind: function (el) { // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
// 注意: 在每个 函数中,第一个参数,永远是 el ,表示 被绑定了指令的那个元素,这个 el 参数,是一个原生的JS对象
// 在元素 刚绑定了指令的时候,还没有 插入到 DOM中去,这时候,调用 focus 方法没有作用
// 因为,一个元素,只有插入DOM之后,才能获取焦点
// el.focus()
},
inserted: function (el) { // inserted 表示元素 插入到DOM中的时候,会执行 inserted 函数【触发1次】
el.focus()
// 和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
},
updated: function (el) { // 当VNode更新的时候,会执行 updated, 可能会触发多次
}
})
// 自定义一个 设置字体颜色的 指令
Vue.directive('color', {
// 样式,只要通过指令绑定给了元素,不管这个元素有没有被插入到页面中去,这个元素肯定有了一个内联的样式
// 将来元素肯定会显示到页面中,这时候,浏览器的渲染引擎必然会解析样式,应用给这个元素
bind: function (el, binding) {
// el.style.color = 'red'
// console.log(binding.name)
// 和样式相关的操作,一般都可以在 bind 执行
// console.log(binding.value)
// console.log(binding.expression)
el.style.color = binding.value
}
})
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
keywords: '', // 搜索的关键字
list: [
{ id: 1, name: '奔驰', ctime: new Date() },
{ id: 2, name: '宝马', ctime: new Date() }
]
},
methods: {
add() { // 添加的方法
var car = { id: this.id, name: this.name, ctime: new Date() }
this.list.push(car)
this.id = this.name = ''
},
del(id) { // 根据Id删除数据
var index = this.list.findIndex(item => {
if (item.id == id) {
return true;
}
})
this.list.splice(index, 1)
},
search(keywords) { // 根据关键字,进行数据的搜索
/* var newList = []
this.list.forEach(item => {
if (item.name.indexOf(keywords) != -1) {
newList.push(item)
}
})
return newList */
// 注意: forEach some filter findIndex 这些都属于数组的新方法,
// 都会对数组中的每一项,进行遍历,执行相关的操作;
return this.list.filter(item => {
// if(item.name.indexOf(keywords) != -1)
// 注意 : ES6中,为字符串提供了一个新方法,叫做 String.prototype.includes('要包含的字符串')
// 如果包含,则返回 true ,否则返回 false
// contain
if (item.name.includes(keywords)) {
return item
}
})
// return newList
}
}
});
</script>
</body>
</html>
Vue中的动画
https://cn.vuejs.org/v2/guide/transitions.html
使用Vue官方提供的transition元素,把需要被动画控制的元素,包裹起来。v-enter和v-leave-to:是一个时间点,是进入之前,元素的起始状态,此时还没有开始进入。v-enter-active:入场动画的时间段。v-leave-active:离场动画的时间段。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
.v-enter,
.v-leave-to{
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active{
transition: all 0.8s ease;
}
</style>
</head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag"/>
<transition>
<h3 v-if="flag">我是h3</h3>
</transition>
</div>
<script>
new Vue({
el: '#app',
data: {
flag:false
}
})
</script>
</body>
</html>
自定义v-前缀
给transition加个name属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
.my-enter,
.my-leave-to{
opacity: 0;
transform: translateX(150px);
}
.my-enter-active,
.my-leave-active{
transition: all 0.8s ease;
}
</style>
</head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag"/>
<transition name="my">
<h3 v-if="flag">我是h3</h3>
</transition>
</div>
<script>
new Vue({
el: '#app',
data: {
flag:false
}
})
</script>
</body>
</html>
使用第三方animate.css类库实现动画
官网
https://daneden.github.io/animate.css/
举个栗子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<link rel="stylesheet" href="https://unpkg.com/animate.css@3.5.2/animate.min.css"/>
</head>
<body>
<div id="app">
<input type="button" value="toggle" @click="flag=!flag"/>
<transition enter-active-class="bounceIn" leave-active-class="bounceOut"
:duration="{ enter: 200, leave: 400}">
<h3 v-if="flag" class="animated">我是h3</h3>
</transition>
</div>
<script>
new Vue({
el: '#app',
data: {
flag:false
}
})
</script>
</body>
</html>
动画钩子
https://cn.vuejs.org/v2/guide/transitions.html#JavaScript-%E9%92%A9%E5%AD%90
动画钩子函数的第一个参数:el,表示要执行动画的那个DOM元素,是个原生的JS DOM对象。可以认为,el是通过document.getElementById(‘’) 方式获取到的原生JS DOM对象。
- beforeEnter 表示动画入场之前,此时,动画尚未开始,可以在beforeEnter中,设置元素开始动画之前的起始样式
- enter表示动画开始之后的样式,这里可以设置小球完成动画之后的,结束状态
- el.offsetWidth没有实际的作用,但是,如果不写,出不来动画效果
- done,起始就是afterEnter这个函数,也就是说:done是afterEnter函数的引用
- 动画完成之后,会调用afterEnter
举个例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
.ball {
width: 15px;
height: 15px;
border-radius: 50%;
background-color: red;
}
</style>
</head>
<body>
<div id="app">
<input type="button" value="购物车" @click="flag=!flag">
<!-- 1. 使用 transition 元素把 小球包裹起来 -->
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter">
<div class="ball" v-show="flag"></div>
</transition>
</div>
<script>
new Vue({
el: '#app',
data: {
flag:false
},
methods: {
beforeEnter: function (el) {
el.style.transform = "translate(0, 0)"
},
enter: function (el, done) {
el.offsetWidth
el.style.transform = "translate(150px, 450px)"
el.style.transition = 'all 1s ease'
done()
},
afterEnter: function (el) {
this.flag = !this.flag
}
}
})
</script>
</body>
</html>
v-for的列表过渡
在实现列表过渡的时候,如果需要过渡的元素,是通过v-for循环渲染出来的,不能使用transition包裹,需要使用transitionGroup。如果要为 v-for循环创建的元素设置动画,必须为每一个元素设置 :key 属性。给transition-group添加appear属性,实现页面刚展示出来时候,入场时候的效果。通过为transition-group元素,设置tag属性,指定transition-group渲染为指定的元素,如果不指定tag属性,默认渲染为span标签。
<transition-group>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的v-move
特性,它会在元素的改变定位的过程中应用。v-move
和v-leave-active
结合使用,能够让列表的过渡更加平缓柔和。
举个例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
li {
border: 1px dashed #999;
margin: 5px;
line-height: 35px;
padding-left: 5px;
font-size: 12px;
width: 100%;
}
li:hover {
background-color: hotpink;
transition: all 0.8s ease;
}
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateY(80px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.6s ease;
}
.v-move {
transition: all 0.6s ease;
}
.v-leave-active {
position: absolute;
}
</style>
</head>
<body>
<div id="app">
<div>
<label>
Id:
<input type="text" v-model="id">
</label>
<label>
Name:
<input type="text" v-model="name">
</label>
<input type="button" value="添加" @click="add">
</div>
<transition-group appear tag="ul">
<li v-for="(item, i) in list" :key="item.id" @click="del(i)">
{{item.id}} --- {{item.name}}
</li>
</transition-group>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
id: '',
name: '',
list: [
{id: 1, name: 'Tom'},
{id: 2, name: 'Jim'},
{id: 3, name: 'Lucy'}
]
},
methods: {
add: function () {
this.list.push({id: this.id, name: this.name})
this.id = this.name = ''
},
del: function (i) {
this.list.splice(i, 1)
}
}
});
</script>
</body>
</html>
组件
组件:以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可。
组件化:是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;
模块化:是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一。
全局组件定义的三种方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<my-com1></my-com1>
<mycom1></mycom1>
<mycom2></mycom2>
<mycom3></mycom3>
<mycom4></mycom4>
</div>
<!-- 在被控制的 #app 外面,使用template元素,定义组件的HTML模板结构 -->
<template id="tmpl">
<div>
<h1>这是通过 template 元素,在外部定义的组件结构,这个方式,有代码的只能提示和高亮</h1>
<h4>好用,不错!</h4>
</div>
</template>
<script>
//方式一:
//1.1 使用 Vue.extend 来创建全局的Vue组件
var com1 = Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>' // 通过 template 属性,指定了组件要展示的HTML结构
})
// 1.2 使用 Vue.component('组件的名称', 创建出来的组件模板对象)
Vue.component('myCom1', com1)
// 如果使用 Vue.component 定义全局组件的时候,组件名称使用了 驼峰命名,则在引用组件的时候,需要把大写的驼峰改为小写的字母,同时,两个单词之前,使用 - 链接;
// 如果不使用驼峰,则直接拿名称来使用即可;
Vue.component('mycom1', com1)
// Vue.component 第一个参数:组件的名称,将来在引用组件的时候,就是一个 标签形式 来引入 它的;第二个参数: Vue.extend 创建的组件
Vue.component('mycom2', Vue.extend({
template: '<h3>这是使用 Vue.extend 创建的组件</h3>'
}))
//方式二:
// 注意:不论是哪种方式创建出来的组件,组件的 template 属性指向的模板内容,必须有且只能有唯一的一个根元素
Vue.component('mycom3', {
template: '<div><h3>这是直接使用 Vue.component 创建出来的组件</h3><span>123</span></div>'
})
// 方式三:
Vue.component('mycom4', {
template: '#tmpl'
})
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
注意:不论是哪种方式创建出来的组件,组件的template属性指向的模板内容,必须有且只能有唯一的一个根元素。
私有组件定义
通过components
定义私有组件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<login></login>
</div>
<template id="tmpl2">
<h3>这是私有组件</h3>
</template>
<script>
var vm = new Vue({
el: '#app',
components: { // 定义实例内部私有组件的
login: {
//方式一:
// template: '<h3>这是私有组件</h3>',
//方式二:
template: '#tmpl2'
}
}
});
</script>
</body>
</html>
组件中的data和methods
组件可以有自己的data数据,实例中的data可以为一个对象,但是组件中的data必须是一个方法,这个方法内部,还必须返回一个对象才行。组件中 的data数据,使用方式,和实例中的data使用方式完全一样。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<counter></counter>
<hr>
<counter></counter>
</div>
<template id="tmpl">
<div>
<input type="button" value="+1" @click="increment">
<h3>{{count}}</h3>
</div>
</template>
<script>
// 这是一个计数器的组件, 身上有个按钮,每当点击按钮,让 data 中的 count 值 +1
Vue.component('counter', {
template: '#tmpl',
data: function () {
// return dataObj
return { count: 0 }
},
methods: {
increment() {
this.count++
}
}
})
var vm = new Vue({
el: '#app'
});
</script>
</body>
</html>
组件切换
v-if方式切换:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<a href="" @click.prevent="flag=true">登录</a>
<a href="" @click.prevent="flag=false">注册</a>
<login v-if="flag"></login>
<register v-else="flag"></register>
</div>
<script>
Vue.component('login', {
template: '<h3>登录组件</h3>'
})
Vue.component('register', {
template: '<h3>注册组件</h3>'
})
var vm = new Vue({
el: '#app',
data: {
flag: false
}
});
</script>
</body>
</html>
使用component元素实现:component是一个占位符,:is
属性可以用来指定要展示的组件的名称
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<component :is="comName"></component>
</div>
<script>
// 组件名称是 字符串
Vue.component('login', {
template: '<h3>登录组件</h3>'
})
Vue.component('register', {
template: '<h3>注册组件</h3>'
})
var vm = new Vue({
el: '#app',
data: {
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
}
});
</script>
</body>
</html>
组件切换动画
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<link rel="stylesheet" href="https://unpkg.com/animate.css@3.5.2/animate.min.css"/>
<style>
.v-enter,
.v-leave-to {
opacity: 0;
transform: translateX(150px);
}
.v-enter-active,
.v-leave-active {
transition: all 0.5s ease;
}
</style>
</head>
<body>
<div id="app">
<a href="" @click.prevent="comName='login'">登录</a>
<a href="" @click.prevent="comName='register'">注册</a>
<!-- 通过 mode 属性,设置组件切换时候的模式 -->
<transition mode="out-in">
<component :is="comName"></component>
</transition>
</div>
<script>
// 组件名称是 字符串
Vue.component('login', {
template: '<h3>登录组件</h3>'
})
Vue.component('register', {
template: '<h3>注册组件</h3>'
})
var vm = new Vue({
el: '#app',
data: {
comName: 'login' // 当前 component 中的 :is 绑定的组件的名称
}
});
</script>
</body>
</html>
父组件向子组件传值
组件中,默认无法访问到父组件中的data上的数据和methods中的方法。父组件可以在引用子组件的时候,通过属性绑定v-bind:
的形式, 把需要传递给子组件的数据,以属性绑定的形式,传递到子组件内部,供子组件使用。组件中的所有props中的数据,都是通过父组件传递给子组件的。
注意:props中的数据,都是只读的,无法重新赋值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<com1 v-bind:parentmsg="msg"></com1>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: '这是父组件中的数据'
},
components: {
com1: {
template: '<h1 @click="change">这是子组件 --- {{ parentmsg }}</h1>',
props: ['parentmsg']// 把父组件传递过来的 parentmsg 属性,先在 props 数组中,定义一下,这样,才能使用这个数据
}
}
});
</script>
</body>
</html>
父组件向子组件传方法
父组件向子组件传递方法,使用的是事件绑定机制:v-on, 当我们自定义了一个事件属性之后,那么子组件就能够通过某些方式,来调用传递进去的 这个方法了。子组件通过$emit
调用父组件方法。
子组件调用父组件的方法,并传值:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<com2 @func="show"></com2>
</div>
<template id="tmpl">
<div>
<h1>这是子组件</h1>
<input type="button" value="子组件按钮-触发父组件传递过来的方法" @click="myclick">
</div>
</template>
<script>
var com2 = {
template: '#tmpl',
data() {
return {
sonmsg: { name: '子组件data', age: 16 }
}
},
methods: {
myclick() {
this.$emit('func', this.sonmsg)
}
}
}
var vm = new Vue({
el: '#app',
data: {
datamsgFormSon: null
},
methods: {
show(data) {
this.datamsgFormSon = data
console.log(this.datamsgFormSon)
}
},
components: {
com2
}
});
</script>
</body>
</html>
组件案例-评论功能实现
存放在localStorage
中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css">
</head>
<body>
<div id="app">
<cmt-box @func="loadComments"></cmt-box>
<ul class="list-group">
<li class="list-group-item" v-for="item in list" :key="item.id">
<span class="badge">评论人: {{ item.user }}</span>
{{ item.content }}
</li>
</ul>
</div>
<template id="tmpl">
<div>
<div class="form-group">
<label>评论人:</label>
<input type="text" class="form-control" v-model="user">
</div>
<div class="form-group">
<label>评论内容:</label>
<textarea class="form-control" v-model="content"></textarea>
</div>
<div class="form-group">
<input type="button" value="发表评论" class="btn btn-primary" @click="postComment">
</div>
</div>
</template>
<script>
var commentBox = {
data() {
return {
user: '',
content: ''
}
},
template: '#tmpl',
methods: {
postComment() {
var comment = { id: Date.now(), user: this.user, content: this.content }
// 从 localStorage 中获取所有的评论
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
list.unshift(comment)
// 重新保存最新的 评论数据
localStorage.setItem('cmts', JSON.stringify(list))
this.user = this.content = ''
this.$emit('func')
}
}
}
var vm = new Vue({
el: '#app',
data: {
list: []
},
created(){
this.loadComments()
},
methods: {
loadComments() { // 从本地的 localStorage 中,加载评论列表
var list = JSON.parse(localStorage.getItem('cmts') || '[]')
this.list = list
}
},
components: {
'cmt-box': commentBox
}
});
</script>
</body>
</html>
通过refs获取元素和组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="button" value="获取元素" @click="getElement" ref="mybtn">
<h3 ref="myh3">哈哈哈, 今天天气太好了!!!</h3>
<login ref="mylogin"></login>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>',
data() {
return {
msg: 'son msg'
}
},
methods: {
show() {
console.log('调用了子组件的方法')
}
}
}
var vm = new Vue({
el: '#app',
data: {},
methods: {
getElement() {
console.log(this.$refs.myh3.innerText)
console.log(this.$refs.mylogin.msg)
this.$refs.mylogin.show()
}
},
components: {
login
}
});
</script>
</body>
</html>
路由
后端路由:对于普通的网站,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源;
前端路由:对于单页面应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,hash有一个特点:HTTP请求中不会包含hash相关的内容;所以,单页面程序中的页面跳转主要用hash实现;在单页面应用程序中,这种通过hash改变来切换页面的方式,称作前端路由。
网站介绍
https://router.vuejs.org/zh/
基本使用
router-view是vue-router提供的元素,专门用来当作占位符的,匹配到的组件,就会展示到router-view中去。
routes:路由匹配规则,每个路由规则,都是一个对象,有两个必须的属性: 属性1是path,表示监听哪个路由链接地址;属性2是component,表示,如果路由是前面匹配到的path,则展示component属性对应的那个组件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<a href="#/login">登录</a>
<a href="#/register">注册</a>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录组件</h1>'
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
{path: '/login', component: login},
{path: '/register', component: register}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
});
</script>
</body>
</html>
router-link
<router-link to="/login" tag="span">登录</router-link>
<router-link to="/register">注册</router-link>
重定向
routes: [ // 路由匹配规则
{ path: '/', redirect: '/login' }, // 这里的 redirect 和 Node 中的 redirect 完全是两码事
{ path: '/login', component: login },
{ path: '/register', component: register }
]
选中路由高亮
方式一:router-link标签中,默认情况
<style>
.router-link-active{
color: red;
font-weight: 800;
font-style: italic;
font-size: 18px;
text-decoration: underline;
}
</style>
方式二:修改router-link-active为myactive,然后再定义样式
var routerObj = new VueRouter({
linkActiveClass: 'myactive'
})
路由传参
方式一:query方式传递参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login?id=10&name=zs">登录</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录组件 --- {{ $route.query.id }} --- {{ $route.query.name }}</h1>',
created(){ // 组件的生命周期钩子函数
console.log(this.$route.query.id)
}
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
{path: '/login', component: login}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
});
</script>
</body>
</html>
方式二:params方式传递参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login/10/zs">登录</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h1>登录组件 --- {{ $route.params.id }} --- {{ $route.params.name }}</h1>',
created(){ // 组件的生命周期钩子函数
console.log(this.$route.params.id)
}
}
var register = {
template: '<h1>注册组件</h1>'
}
var routerObj = new VueRouter({
routes: [
{path: '/login/:id/:name', component: login}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router: routerObj // 将路由规则对象,注册到 vm 实例上,用来监听 URL 地址的变化,然后展示对应的组件
});
</script>
</body>
</html>
路由嵌套
使用children属性,实现子路由。同时,子路由的path前面,不要带/
,否则永远以根路径开始请求,这样不方便我们用户去理解URL地址。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/account">Account</router-link>
<router-view></router-view>
</div>
<template id="tmpl">
<div>
<h1>这是 Account 组件</h1>
<router-link to="/account/login">登录</router-link>
<router-link to="/account/register">注册</router-link>
<router-view></router-view>
</div>
</template>
<script>
var account = {
template: '#tmpl'
}
var login = {
template: '<h3>登录</h3>'
}
var register = {
template: '<h3>注册</h3>'
}
var router = new VueRouter({
routes: [
{
path: '/account',
component: account,
children: [
{ path: 'login', component: login },
{ path: 'register', component: register }
]
}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
</script>
</body>
</html>
命名视图实现经典布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
<style>
html,
body {
margin: 0;
padding: 0;
}
.header {
background-color: orange;
height: 80px;
}
h1 {
margin: 0;
padding: 0;
font-size: 16px;
}
.container {
display: flex;
height: 600px;
}
.left {
background-color: lightgreen;
flex: 2;
}
.main {
background-color: lightpink;
flex: 8;
}
</style>
</head>
<body>
<div id="app">
<router-view></router-view>
<div class="container">
<router-view name="left"></router-view>
<router-view name="main"></router-view>
</div>
</div>
<script>
var header = {
template: '<h1 class="header">Header头部区域</h1>'
}
var leftBox = {
template: '<h1 class="left">Left侧边栏区域</h1>'
}
var mainBox = {
template: '<h1 class="main">mainBox主体区域</h1>'
}
var router = new VueRouter({
routes: [
{
path: '/', components: {
'default': header,
'left': leftBox,
'main': mainBox
}
}
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
router
});
</script>
</body>
</html>
watch
可以监视 data 中指定数据的变化,然后触发这个 watch 中对应的 function 处理函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="firstname"> +
<input type="text" v-model="lastname"> =
<input type="text" v-model="fullname">
</div>
<script>
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
el: '#app',
data: {
firstname: '',
lastname: '',
fullname: ''
},
methods: {},
watch: {
'firstname': function (newVal, oldVal) {
// this.fullname = this.firstname + '-' + this.lastname
this.fullname = newVal + '-' + this.lastname
},
'lastname': function (newVal) {
this.fullname = this.firstname + '-' + newVal
}
}
});
</script>
</body>
</html>
watch监视路由地址的改变
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script src="./lib/vue-router-3.0.1.js"></script>
</head>
<body>
<div id="app">
<router-link to="/login">登录</router-link>
<router-link to="/register">注册</router-link>
<router-view></router-view>
</div>
<script>
var login = {
template: '<h3>这是登录子组件</h3>'
}
var register = {
template: '<h3>这是注册子组件</h3>'
}
var router = new VueRouter({
routes: [
{ path: '/', redirect: '/login' },
{ path: '/login', component: login },
{ path: '/register', component: register }
]
})
var vm = new Vue({
el: '#app',
data: {},
methods: {},
// router: router
router,
watch: {
'$route.path': function (newVal, oldVal) {
// console.log(newVal + ' --- ' + oldVal)
if (newVal === '/login') {
console.log('欢迎进入登录页面')
} else if (newVal === '/register') {
console.log('欢迎进入注册页面')
}
}
}
});
</script>
</body>
</html>
computed计算属性
计算属性关键词:computed。computed是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值。而使用methods,在重新渲染的时候,函数总会重新调用执行。
在computed中,可以计算属性,本质是一个方法,只不过,我们在使用这些计算属性的时候,是把它们的名称,直接当作属性来使用的,并不会把计算属性,当作方法去调用。
可以说使用 computed 性能会更好,但是如果你不希望缓存,你可以使用 methods 属性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="firstname"> +
<input type="text" v-model="middlename"> +
<input type="text" v-model="lastname"> =
<input type="text" v-model="fullname">
<p>{{ fullname }}</p>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
firstname: '',
lastname: '',
middlename: ''
},
methods: {},
computed: {
'fullname': function () {
console.log('ok')
return this.firstname + '-' + this.middlename + '-' + this.lastname
}
}
});
</script>
</body>
</html>
watch
、computed
和methods
之间的对比
computed
属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;methods
方法表示一个具体的操作,主要书写业务逻辑;watch
一个对象,键是需要观察的表达式,值是对应回调函数。主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作;可以看作是computed
和methods
的结合体;
webpack
webpack可以解决各个包之间的复杂依赖关系。webpack是基于Node.js开发出来的一个前端项目自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能。
使用Gulp,是基于task任务的;使用Webpack,是基于整个项目进行构建的。
官网
http://webpack.github.io/
webpack安装的两种方式
运行npm i webpack -g
全局安装webpack,这样就能在全局使用webpack的命令
在项目根目录中运行npm i webpack --save-dev
安装到项目依赖中
配置文件打包命令
创建webpack.config.js
文件:
// 导入处理路径的模块
var path = require('path');
// 导出一个配置对象,将来webpack在启动的时候,会默认来查找webpack.config.js,并读取这个文件中导出的配置对象,来进行打包处理
module.exports = {
entry: path.join(__dirname, 'src/js/main.js'), // 项目入口文件
output: { // 配置输出选项
path: path.join(__dirname, 'dist'), // 配置输出的路径
filename: 'bundle.js' // 配置输出的文件名
}
}
最后控制台执行webpack。
webpack-dev-server实时打包构建
使用webpack-dev-server
来实现代码实时打包编译,当修改代码之后,会自动进行打包构建。
运行cnpm i webpack-dev-server --save-dev
安装到开发依赖
在package.json
文件中,在scripts
节点下新增"dev": "webpack-dev-server"
指令,然后执行npm run dev
,发现可以进行实时打包。
但是dist目录下并没有生成bundle.js
文件,这是因为webpack-dev-server
将打包好的文件放在了内存中。把bundle.js
放在内存中的好处是:由于需要实时打包编译,所以放在内存中速度会非常快。
这个时候访问webpack-dev-server启动的http://localhost:8080/
网站,发现是一个文件夹的面板,需要点击到src目录下,才能打开我们的index首页,此时引用不到bundle.js文件,需要修改index.html中script的src属性为:<script src="../bundle.js"></script>
为了直接访问到index首页,可以指定启动的根目录:
"dev": "webpack-dev-server --contentBase src"
同时修改index页面中script的src属性为<script src="bundle.js"></script>
配置package.json
修改package.json
的script节点如下,其中--open
表示自动打开浏览器,--port 4321
表示打开的端口号为4321,--hot
表示启用浏览器热更新,--contentBase src
表示直接访问到index首页:
"dev": "webpack-dev-server --hot --port 4321 --open --contentBase src"
配置webpack.config.js
webpack.config.js
文件,新增devServer
节点如下:
devServer:{
hot:true,
open:true,
port:4321,
contentBase:'src'
}
在头部引入webpack
模块:
var webpack = require('webpack');
在plugins
节点下新增:
plugins:[
new webpack.HotModuleReplacementPlugin()
]
Vue2.5开发去哪儿网App
TODOList
指令,组件化,父子组件的传值,的使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TODOList</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="inputValue"/>
<button v-on:click="handleBtnClick">提交</button>
<ul>
<todo-item v-bind:content="item"
v-bind:index = "index"
v-for="(item, index) in list"
@delete="handleItemDelete">
</todo-item>
</ul>
</div>
<script>
//全局组件
// Vue.component("TodoItem", {
// props: ['content'],
// template: "<li>{{content}}</li>"
// })
//局部组件
var TodoItem = {
props: ['content','index'],
template: "<li @click='handleItemDelete'>{{content}}</li>",
methods: {
handleItemDelete: function (){
this.$emit("delete", this.index);
}
}
}
var app = new Vue({
el: "#app",
//局部组件-注册
components: {
TodoItem: TodoItem
},
data: {
list: [],
inputValue: ""
},
methods: {
handleBtnClick: function () {
this.list.push(this.inputValue)
this.inputValue = ''
},
handleItemDelete: function (index){
this.list.splice(index, 1)
}
}
})
</script>
</body>
</html>
setter和getter
setter和getter:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
{{fullName}}
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
firstName: "Dell",
lastName: "Lee"
},
computed: {
fullName: {
get: function (){
return this.firstName + " " + this.lastName
},
set: function (value){
var arr = value.splice(" ");
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
</body>
</html>
样式的绑定
点击颜色切换。
class方式一:定义class的名字是activated,但是class是否有名字是根据isActivated:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
.activated {
color: red;
}
</style>
</head>
<body>
<div id="app">
<div @click="handleDivClick" :class="{activated: isActivated}">
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
isActivated: false
},
methods: {
handleDivClick: function (){
this.isActivated = !this.isActivated
}
}
})
</script>
</body>
</html>
class方式二:定义class的名字的key是activated,切换名字的value是否为activated和空值:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<style>
.activated {
color: red;
}
</style>
</head>
<body>
<div id="app">
<div @click="handleDivClick" :class="[activated]">
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
activated: ""
},
methods: {
handleDivClick: function (){
this.activated = this.activated === "activated" ? "" : "activated"
}
}
})
</script>
</body>
</html>
内联style样式,方式一:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div @click="handleDivClick" :style="styleObj">
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
styleObj: {
color: "black"
}
},
methods: {
handleDivClick: function () {
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black"
}
}
})
</script>
</body>
</html>
内联style样式,方式二:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div @click="handleDivClick" :style=[styleObj]>
Hello World
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
styleObj: {
color: "black"
}
},
methods: {
handleDivClick: function () {
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black"
}
}
})
</script>
</body>
</html>
组件化中的is
组件化中的is使用:当某些标签下必须要固定标签,这时候使用组件化,可以用is:
比如table下的tr,ul下的li,ol下的li,select下的option,等等
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
</div>
<script>
Vue.component(
"row",{
template: "<tr><td>this is a row</td></tr>"
}
)
var vm = new Vue({
el: "#app"
})
</script>
</body>
</html>
组件化中的refs
子组件通过change通知父组件发生改变,利用refs获取document:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<counter ref="one" @change="handleChange"></counter>
<counter ref="two" @change="handleChange"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component(
"counter",{
template: "<div @click='handlerClick'>{{number}}</div>",
data: function (){
return {number: 0}
},
methods: {
handlerClick: function (){
this.number ++
this.$emit('change')
}
}
}
)
var vm = new Vue({
el: "#app",
data: {
total: 0
},
methods: {
handleChange: function (){
this.total = this.$refs.one.number + this.$refs.two.number
}
}
})
</script>
</body>
</html>
父子组件之间传值
父组件通过属性形式,向子组件传值,子组件通过props接收。
单向数据流:父组件向子组件传值的参数,子组件不要修改,可以让子组件用变量接收后,子组件修改变量。
子组件向父组件传值,通过emit事件的形式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<counter :count="3" @inc="handleIncrease"></counter>
<counter :count="2" @inc="handleIncrease"></counter>
<div>{{total}}</div>
</div>
<script>
var counter = {
props: ['count'],
data: function (){
return{number: this.count}
},
template: '<div @click="handleClick">{{number}}</div>',
methods: {
handleClick: function (){
this.number = this.number + 2;
this.$emit('inc',2)
}
}
}
var vm = new Vue({
el: "#app",
components: {
counter: counter
},
data: {
total: 5
},
methods: {
handleIncrease: function (step){
this.total += step
}
}
})
</script>
</body>
</html>
子组件props
约束父组件传递来的content必须为String:
props: {
content: String
}
另一种写法:
props: {
content: {
type: String
}
}
约束父组件传递来的content可以为数字或者字符串:
props: {
content: [Number, String]
}
约束父组件必须传content:
props: {
content: {
type: String,
required: true
}
}
设置父组件传递的content有默认值:
props: {
content: {
type: String,
required: false,
default: 'default value'
}
}
约束父组件传递来的content长度大于5:
props: {
content: {
type: String,
validator: function (value){
return (value.length > 5)
}
}
}
组件绑定原生事件
在子组件中实现父组件中的点击事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<child @click="handlerClick"></child>
</div>
<script>
Vue.component('child', {
template: '<div @click="handlerChildClick">Child</div>',
methods: {
handlerChildClick: function () {
this.$emit('click')
}
}
})
var vm = new Vue({
el: "#app",
methods: {
handlerClick: function () {
alert("click")
}
}
})
</script>
</body>
</html>
组件绑定原生事件,另有一种native写法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<child @click.native="handlerClick"></child>
</div>
<script>
Vue.component('child', {
template: '<div>Child</div>'
})
var vm = new Vue({
el: "#app",
methods: {
handlerClick: function () {
alert("click")
}
}
})
</script>
</body>
</html>
非父子组件传值
Bus/事件总线/发布订阅模式/观察者模式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<child content="Dell"></child>
<child content="Lee"></child>
</div>
<script>
Vue.prototype.bus = new Vue()
Vue.component('child', {
data: function (){
return {
selfContent: this.content
}
},
props: {
content: String
},
template: '<div @click="handleClick">{{selfContent}}</div>',
methods: {
handleClick:function (){
this.bus.$emit('change', this.selfContent)
}
},
mounted: function (){
var this_ = this
this.bus.$on('change',function (msg){
this_.selfContent = msg
})
}
})
var vm = new Vue({
el: "#app"
})
</script>
</body>
</html>
插槽slot
向子组件中插入标签:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<body-content>
<div class="header" slot="header">header</div>
<div class="footer" slot="footer">footer</div>
</body-content>
</div>
<script>
Vue.component('body-content', {
template: '<div><slot name="header"></slot><div class="content">content</div><slot name="footer"></slot></div>'
})
var vm = new Vue({
el: "#app"
})
</script>
</body>
</html>
作用域插槽slot-scope:
父组件向子组件插入标签,子组件向父组件传值item:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
</head>
<body>
<div id="app">
<body-content>
<template slot-scope="props">
<li>{{props.item}}</li>
</template>
</body-content>
</div>
<script>
Vue.component('body-content', {
data: function (){
return {list:[1,2,3,4]}
},
template: '<div><ul><slot v-for="item of list" :item=item></slot></ul></div>'
})
var vm = new Vue({
el: "#app"
})
</script>
</body>
</html>
项目准备
命令行工具:
node -v
npm -v
git --version
npm install --global vue-cli
vue init webpack 项目文件夹
//运行项目-项目目录下
npm run dev
npm run start
代码结构介绍:
package.json---------------------项目依赖
package-lock.json----------------依赖具体版本
index.html-----------------------首页模板文件
.postcssrc.js--------------------postcssrc配置项
.eslintrc.js---------------------规范
.eslintignore--------------------eslint检测忽略
.editorconfig--------------------编译器语法
.babelrc-------------------------babelrc配置
static---------------------------静态资源
node_modules---------------------依赖包
src-main.js----------------------入口
src-App.vue----------------------根组件-单文件组件
src-router-----------------------路由
src-components-------------------小组件
src-assets-----------------------项目图片资源
config-index.js------------------基础配置
config-dev.env.js----------------开发环境配置
config-prod.env.js---------------线上环境配置
build文件------------------------打包配置
fastclick-解决移动端三秒延迟:
npm install fastclick --save
stylus和stylus-loader:
npm install stylus --save
npm install stylus-loader --save
轮播插件:
npm install vue-awesome-swiper@2.6.7 --save
axios:
npm install axios --save
better-scroll:
npm install better-scroll --save
vuex:
npm install vuex --save
babel-polyfill:
npm install babel-polyfill --save
打包:
npm run build
京东金融
项目准备
nvm:
//mac
https://github.com/nvm-sh/nvm
//windows
https://github.com/coreybutler/nvm-windows
git:
https://www.bootcss.com/p/git-guide/
webpack:
https://webpack.docschina.org/
https://www.webpackjs.com/
babel:
https://www.babeljs.cn/
atom:
https://atom.io/
eslint:
http://eslint.cn/docs/user-guide/configuring
es6:
http://es6-features.org/#Constants
sass:
https://sass.bootcss.com/
vue:
https://cn.vuejs.org
项目
swiper:
npm i vue-awesome-swiper
打包:
npm run build
js压缩:
npm i -D uglifyjs-webpack-plugin
css压缩:
//css-loader
minimize: true
线上调试
source map
构建工具
webpack.config.js文件中:
入口:
entry: {
app: './app/js/main.js'
}
配置解析构建:
module: {
loaders: [
{
test: /\.html$/,
loader: 'html-loader'
}, {
test: /\.vue$/,
loader: 'vue-loader',
}, {
test: /\.scss$/,
loader: 'style-loader!css-loader!sass-loader'
}
]
},
插件:
plugins:[]
输出:
output: {
filename: '[name].min.js',
path: path.resolve(__dirname, 'dist')
}
devServer(端口等配置):
devServer: {
contentBase: './dist',
hot: true,
compress: true,
port: 9000,
clientLogLevel: "none",
quiet: true
},
配置:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
resolve:
resolve: {
extensions: [
'.js', '.vue', '.json'
],
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
头条
初始化项目
脚手架:
npm install --global @vue/cli
创建项目:
vue create toutiao-vue
Vue CLI v4.5.13
? Please pick a preset: Manually select features
? Check the features needed for your project:
(*) Choose Vue version
(*) Babel
( ) TypeScript
( ) Progressive Web App (PWA) Support
(*) Router
(*) Vuex
(*) CSS Pre-processors
>(*) Linter / Formatter
( ) Unit Testing
( ) E2E Testing
? Choose a version of Vue.js that you want to start the project with 2.x
? Use history mode for router? (Requires proper server setup for index fallback in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Less
? Pick a linter / formatter config: Standard
? Pick additional lint features:
(*) Lint on save
>(*) Lint and fix on commit (requires Git)
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)n
运行项目:
npm run serve
vant:
http://github.com/youzan/vant
https://youzan.github.io/vant/#/zh-CN/
Rem 布局适配:
npm i amfe-flexible
//main.js
import 'amfe-flexible'
//-D 是 --save-dev 的简写
npm install postcss-pxtorem -D
npm install postcss-pxtorem@5.1.1 -D
//项目根目录中创建 `.postcssrc.js` 文件
module.exports = {
plugins: {
'autoprefixer': {
browsers: ['Android >= 4.0', 'iOS >= 8']
},
'postcss-pxtorem': {
rootValue: 37.5,
propList: ['*']
}
}
}
注意:该工具的rem转换,行内样式不会转,例如 <div style="width: 200px;"></div>
。
网络封装:
npm i axios
Element
介绍
Element:是饿了么公司前端开发团队提供的一套基于 Vue 的网站组件库,用于快速构建网页。
Element 提供了很多组件(组成网页的部件)供我们使用。例如 超链接、按钮、图片、表格等等~
官网网址是
https://element.eleme.cn/#/zh-CN