vue进阶之路 webpack打包 持续更新
MVVM设计思想
- M(model)
- V(view)
- VM(View-Model)
Vue生命周期
- 挂载(初始化相关属性)
- beforeCreate->在实力初始化之后,数据观测和配置之前被调用
- created->在实力创建完成后被立即调用
- beforeMount->在挂载开始之前被调用
- mounted->被新创建的vm.$el替换,并挂载到实力上之后调用钩子
- 更新(元素或组件的变更操作)
- beforeUpdate->数据更新时调用,发生在虚拟DOM打补丁之前(调用后台接口向模板中填充数据)
- updated->由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该狗子
- 销毁(销毁相关属性)
- beforeDestroy->实例销毁之前调用
- destroyed->实例销毁后调用
数据交互方式:
视图层->监听dom->数据层
数据层->根据数据绑定->视图层
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYIQxm7Z-1596001043673)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-26 上午9.30.20.png)]
Vue->helloWorld
<div>{{msg}}
</div>
new Vue({el:'',data:{msg:'1'}
})
el:元素的挂载位置
data:模型数据
{{}}:差值表达式->将数据填充到HTML标签中,支持基本的计算操作。
Vue 指令
自定义的属性:在标签上定义的data-xxxx/abc = ’ ';
v-cloak 指令
作用:解决差值表达式闪动问题
1.提供样式
[v-cloak]{display:none;
}
2.在差值表达式所在的标签中添加v-cloak
2.在差值表达式所在的标签中添加v-cloak
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EkZPV8XF-1596001043675)(/Users/limboaaa/Desktop/截屏2020-07-26 上午8.54.22.png)]
v-text 指令
描述:向页面中插入一个纯文本
v-html 指令
描述:填充HTML片段
- 存在安全问题
- 本网站内部数据可以使用,来自第三方的数据不可用
v-pre 指令
**描述:**显示原始信息,跳过编译过程
v-once 指令
**描述:**只编译一次,显示内容后不再具有数据响应式功能
应用场景:显示的信息后续不必再修改,可以使用v-once,可以提高性能。
<div v-once>{{msg}}</div>
v-model 指令(双向数据绑定)
<input type="text" v-model= 'msg'>
v-model的本质,就是v-bind:value = ‘’,然后Input监听事件,将input的内容复制给该属性;
<input type="text" v-model= 'msg' @input = 'msg = $event.target.value'>
事件绑定
v-on指令
完全体:
<button v-on:click='num++'>点击</button>
简写:
<button @click='num++'>点击</button>
.stop 阻止冒泡
<div id="app">{{num}}<div @click = 'handle'><button @click.stop='handle1'>点击1</button></div></div>
.prevent 阻止默认行为
<a href="https://www.baidu.com" @click.prevent='handle'>跳转至百度</a>
键盘:enter事件/ delete事件
用户名:<input type="text" v-model='name' @keyup.delete = 'delAll'>
密码:<input type="text" v-model='pwd' @keyup.enter = 'submit'>
自定义按键修饰符
全局变量:config.keyCodes对象
Vue.config.keyCodes.xx = keynum;
使用:<input type="text" v-model='pwd' @keyup.xx = 'handle'>
xx:别名
keynum:->可使用event.keyCode 获取对应键位的值
属性绑定
v-bind指令
完全体:
<a v-bind:href= 'url'></a>
简写形式:
<a :href= 'url'></a>
样式绑定
默认的class如何处理呢?->保留
数组可以结合对象一起使用
<div v-bind:class="[activeClass,errorClass,{test:isTest}]"></div>
使用:
<Style>.active {border: 1px solid red;width: 100px;height: 100px;}.error{background-color: yellow;}</Style>
// 对象语法
<div v-bind:class="{active:isActive,error:isError}"></div>
// 数组语法
<div v-bind:class="[activeClass,errorClass]"></div>// 对象简化操作语法
<div v-bind:class="objClasses"></div>
// 数组简化操作语法
<div v-bind:class="arrClasses"></div>
// 内联Style
<div v-bind:style="{border:borderStyle,width:widthStyle,height:heightStyle}"></div>
// 简化内联Style
<div v-bind:style = 'objStyle'></div>data() {return {/*对象语法 开始**/isActive:true,isError:false/*对象语法 结束**/-----------------------------------------------------/*对象简化操作 开始**/objClasses:{active:true,error:true}/*对象简化操作 结束**/-----------------------------------------------------/*数组语法 开始**/activeClass:'active',errorClass:'error'/*数组语法 结束**/-----------------------------------------------------/*数组简化操作 开始**/arrClasses:['active','error']/*数组简化操作 结束**/-----------------------------------------------------/*内联样式 开始**/borderStyle:'1px solid blue',widthStyle:100px,heightStyle:200px/*内联样式 结束**/-----------------------------------------------------/*简化内联样式 开始**/ objStyle{border:'1px solid blue',width:100px,height:200px}/*简化内联样式 结束**/}
分支结构
- v-if
- v-else
- V-else-if
- v-show
v-if与v-show的区别
- V-if 控制元素是否渲染到页面
- V-show 控制元素是否显示(display:none/’ ')
循环结构
V-for
<ul><!-- 遍历数组 --><li v-for = 'item in user'>{{item}}</li><!-- 可获取数组下标 --><li v-for = '(item,index) in user'>{{item}}{{index}}</li><!-- 数组对象遍历方式 --><li v-for = 'item in user'>{{item.eName}}------{{item.cName}}</li><!-- key的作用:帮助vue区分不同元素,从而提高性能 --><!-- :key -> bind:key; item.id-> 该对象的id(唯一) --><li :key = 'item.id' v-for = '(item,index) in user'>{{item}}</li></ul>data() {return {//数组fruits:['apple','orange','banana']// 数组对象user:[{eName:'jack',cName:"杰克"},{eName:'john',cName:'路西'},{eName:'john',cName:'约翰'}]}}# item:自定义变量,迭代的元素。
分支循环结构
v-for 遍历对象
<li v-for = '(value,key,index) in obj'></li>
v-for 结合v-if
<li v-if= ’value||key||index‘ v-for = '(value,key,index) in obj'></li>
表单域修饰符
- number:转换为数值
- Trim:去掉开始和结尾的空格
- lazy:将input时间切换为change事件
<input v-model.number = 'num'>
<input v-model.trim = 'info'>
// 输入框失去焦点触发,使用场景:注册用户名,会检测是否已经存在,需要获取该输入框数据
<input v-model.lazy = 'info'>
自定义指令
// 全局指令
Vue.directive('指令名'{inserted:function(el){// 获取元素的焦点el.指令名()}
})// 局部指令
directives:{指令名称:{insered:function(el){el.指令名称}}
}
// 使用方式
<input v-指令名>
计算属性
将复杂的计算逻辑抽取出来,使模板内容更加简洁。
计算属性与方法的区别?
- 计算属性是基于他们的依赖(data中的数据)进行缓存的(相同的逻辑只执行一次,并将执行的结果缓存)
- 方法不存在缓存
<div id="app">{{msg}}{{reverseString}}<input type = 'text' v-model = 'msg'></div>computed: {reverseString:function(){// 有返回值 需要return return this.msg.split('').reverse().join('')}}
侦听器
描述:数据变化会触发侦听器锁绑定的方法
使用场景:数据变化时执行异步或开销较大的操作
var vm = new Vue({el: '#app',data() {return {firstName:'张',lastName:'桂源',fullName:'张桂源',}},// 侦听器watch: {// 侦听的属性名与data中的属性名一致// val:变化后的数据firstName:function(val){this.fullName = val + ' ' + this.lastName},lastName:function(val){this.fullName = this.firstName + ' ' + val}},})
过滤器
作用:格式化数据,比如讲字符串格式化为首字母大写,将日期转化为指定格式。
// 创建一个全局的过滤器Vue.filter('filter1',function(val){return val.charAt(0).toUpperCase()+val.slice(1)})// 使用方式{{msg | filter1}}// 可以多个条件{{msg | filter1 | filter2}}// 可以对绑定的数据进行过滤<div v-bind:id = 'id | filter'></div>// 局部filterfilters:{filter1:function(){},filter2:function(){}
}// 时间格式化{{date | dateFormat('yyyy-MM-dd')}}// 第二个形参为传递的参数Vue.filter('dateFormat',function(value,arg){// 时间转换格式})
变异方法(修改原有数据)
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
替换数组(生成新的数组)
filter()、concat()、slice() 不会改变原数组,都返回一个心的数组
vm.list[1] = 'lemon' // 通过这种下表修改属性值,不具备响应式Vue.set(vm.list,index,'lemon') // 具备响应式
vm.$set(vm.list,index,'lenmon') // 具备响应式// 对象
vm.$set(vm.info,'gender','female');// 具备响应式
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head>
<script type="text/javascript" src="./js/vue.js"></script>
<!-- 导入路由js -->
<script type="text/javascript" src="./js/vue-router.js"></script>
<Style>.active {border: 1px solid red;width: 100px;height: 100px;}.error {background-color: yellow;}
</Style><body><div id="app"><div>编号:<input class="form-control" type="text" v-model='id' :disabled="flag">书名:<input class="form-control" type="text" v-model='name'><span>{{total}}</span><button @click='handle' :disabled='butFlag'>添加</button></div><table><tr :key='item.id' v-for='(item,index) in books'><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.now}}</td><td><a href="" @click.prevent='edit(item.id)'>修改</a><span @click.prevent>|</span><a href="" @click.prevent='del(item.id)'>删除</a></td></tr></table></div></body>
<script>// 第二个形参为传递的参数Vue.filter('dateFormat', function (value, arg) {// 时间转换格式})var vm = new Vue({el: '#app',data: {id: '',name: '',flag: false,butFlag: false,books: []},computed: {total: function () {return this.books.length}},watch: {name: function (val) {var flag = this.books.some(function (item) {return item.name == val;});if (flag) {this.butFlag = true;} else {this.butFlag = false;}}},mounted() {this.books = [{id: 1,name: '围城'},{id: 2,name: '三体'},{id: 3,name: '白夜行'}]},methods: {handle: function () {if (this.flag) {// 修改操作this.books.some((item) => {if (item.id == this.id) {item.name = this.namereturn true;}})this.flag = false} else {// 添加var book = {};book.id = this.id;book.name = this.name;this.books.push(book);}// 将文本清空this.id = '';this.name = '';},edit: function (id) {this.flag = true;var book = this.books.filter(function (item) {return item.id == id;})this.id = book[0].id;this.name = book[0].name;},del: function (id) {this.books = this.books.filter(function (item) {return item.id != id})}},})
</script></html>
组件化开发
- 命名方式 短横线:xxx-xxx 驼峰式:HelloWorld ,
- 组件模板必须是唯一的根节点
- 组件data 必须是一个函数(组件可复用且互不影响)
// 使用<button-count></button-count><button-count></button-count><button-count></button-count>// 全局组件注册Vue.component('button-count',{// 组件数据 必须是一个函数data:function(){return {count :0}},// 组件模板内容template:`<button @click = "count++">点击了{{count}}次</button>`})// 局部组件使用
可以声明在外部
var button-count = {data:function(){return{msg:"HelloWorld"}},template:`<div><span>{{msg}}</span></div>`
}var vm = new Vue({el:'',data:{},components:{'button-count':button-count}
})
父组件向子组件传值
组件内部通过Props接受传递过来的值
- props中使用驼峰形式,模板中需要使用短横线的形式
- 字符串形式的模板中没有这个限制(template中)
- props传递原则:
Vue.component('menu-item',{props:['title'], temelate:`<div>{{title}} </div>` })
父组件通过属性将值传递给子组件
<menu-item title = "来自父组件的数据"></menu-item> <menu-item :title = 'title'></menu-item>
子组件向父组件传递信息
- 子组件通过自定义事件向父组件传递信息
`<button @click = '$emit("enlarge-text")'>扩大父组件字体大小</button>`
- 父组件监听子组件的事件
<menu-item @enlarge-text = 'handle'></menu-item>
子组件向父组件传值
`<button @click = '$emit("enlarge-text",0.1)'>扩大父组件字体大小</button>`
父组件监听子组件的事件
<menu-item @enlarge-text = 'fontSize += $event'></menu-item>
非父子组件间传值
- 单独的事件中心管理组件间的通信
var eventHub = new Vue()
- 监听事件与销毁事件
eventHub.$on('add-todo',addTodo)
eventHub.$off('add-todo') // 一般在vue实例中销毁
- 触发事件
eventHub.$emit('add-todo',id)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aqhM5kIn-1596001043677)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-27 下午4.52.04.png)]
组件插槽的作用
父组件向子组件传递内容
<slot>content-text</slot>
购物车案例
var CartTitle = {props: ['uname'],template: `<div class="title">{{uname}}的商品</div>`}var CartList = {props: ['list'],template: `<div><div :key='item.id' v-for='item in list' class="item"><img :src="item.img"/><div class="name">{{item.name}}</div><div class="change"><a href="" @click.prevent='sub(item.id)'>-</a><input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/><a href="" @click.prevent='add(item.id)'>+</a></div><div class="del" @click='del(item.id)'>×</div></div></div>`,methods: {changeNum: function(id, event){this.$emit('change-num', {id: id,type: 'change',num: event.target.value});},sub: function(id){this.$emit('change-num', {id: id,type: 'sub'});},add: function(id){this.$emit('change-num', {id: id,type: 'add'});},del: function(id){// 把id传递给父组件this.$emit('cart-del', id);}}}var CartTotal = {props: ['list'],template: `<div class="total"><span>总价:{{total}}</span><button>结算</button></div>`,computed: {total: function() {// 计算商品的总价var t = 0;this.list.forEach(item => {t += item.price * item.num;});return t;}}}Vue.component('my-cart',{data: function() {return {uname: '张三',list: [{id: 1,name: 'TCL彩电',price: 1000,num: 1,img: 'img/a.jpg'},{id: 2,name: '机顶盒',price: 1000,num: 1,img: 'img/b.jpg'},{id: 3,name: '海尔冰箱',price: 1000,num: 1,img: 'img/c.jpg'},{id: 4,name: '小米手机',price: 1000,num: 1,img: 'img/d.jpg'},{id: 5,name: 'PPTV电视',price: 1000,num: 2,img: 'img/e.jpg'}]}},template: `<div class='cart'><cart-title :uname='uname'></cart-title><cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list><cart-total :list='list'></cart-total></div>`,components: {'cart-title': CartTitle,'cart-list': CartList,'cart-total': CartTotal},methods: {changeNum: function(val) {// 分为三种情况:输入域变更、加号变更、减号变更if(val.type=='change') {// 根据子组件传递过来的数据,跟新list中对应的数据this.list.some(item=>{if(item.id == val.id) {item.num = val.num;// 终止遍历return true;}});}else if(val.type=='sub'){// 减一操作this.list.some(item=>{if(item.id == val.id) {item.num -= 1;// 终止遍历return true;}});}else if(val.type=='add'){// 加一操作this.list.some(item=>{if(item.id == val.id) {item.num += 1;// 终止遍历return true;}});}},delCart: function(id) {// 根据id删除list中对应的数据// 1、找到id所对应数据的索引var index = this.list.findIndex(item=>{return item.id == id;});// 2、根据索引删除对应数据this.list.splice(index, 1);}}});var vm = new Vue({el: '#app',data: {}});
前后端交互
要保证多个异步请求顺序:需要嵌套ajax
Promise
常用APi:
实例方法
- p.then()得到异步任务的正确结果
- p.catch()得到异常信息
- p.finally()成功与否都会执行(尚且不是正式标准)
对象方法
- Promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果
- Promise.race()并发处理多个异步任务,只要有一个任务完成就能得到结果,其他任务仍执行,只是不关心其他任务执行的状态。
异步编程的一种解决方案,从语法上讲,Promise是一个对象,它可以获取异步操作消息
使用Promise 主要有一下好处:
- 可以避免多层异步调用嵌套问题
- Promise对象提供简洁的API,使得控制异步操作更加容易
Promise基本用法
- 实例化Promise对象,构造函数中传递函数,该函数中用于处理异步任务
- resolve和reject 两个参数用于处理成功和失败两种情况,并通过p.then获取处理结果。
var p = new Promise(function(resolve.reject){//成功时调用resolve()//失败时调用reject()
});
p.then(function(ret){// 从resolve 得到正常结果},
function(ret){// 从reject 得到错误信息}
)
实例:
var promise = new Promise(function(resovle,reject){var flag = false;if(flag){// 正确的逻辑resovle('ok')}else{// 错误的逻辑reject('fuck!')}});promise.then(function(msg){console.log(msg)},function(msg){console.log(msg)})-----------------------------------------------------------------function queryData(url){var promise = new Promise(resolve,reject){var xhr = new XMLHttpRequest();if(xhr.readyState != 4)return;if(xhr.readyState == 4 && xhr.status == 200){resolve('success');}else{reject('error')}xhr.open('get',url);xhr.send(null)}return p;}
queryData('url')
.then(function(msg){// 正常时},function(msg){// 异常时}
)
// 异常时
.catch(function(msg){})var p1 = queryData(url)
var p2 = queryData(url)
var p3 = queryData(url)// Promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果
Promise.all([p1,p2,p3]).then(function(result){console.log(result)})
// Promise.race()并发处理多个异步任务,只要有一个任务完成就能得到结果,其他任务仍执行,只是不关心其他任务执行的状态。
Promise.race([p1,p2,p3]).then(function(result){console.log(result)})
then参数中的函数返回值
- 返回Promise实例对象
- 返回该实例对象会调用下一个then
- 返回普通值
- 返回的普通值会直接传递给下一个then,通过then参数中函数的参数接収该值
接口调用fetch用法
基本特性:
- 更加简单的数据获取方式,功能更强大,更灵活,可以看做是xhr的升级版
- 基于Promise实现
响应数据格式:
- text():将返回体处理成字符串类型
- json():将返回结果和JSON.parse(responseText)一样
常用配置选项
- method(string):Htpp的请求方法,[GET/POST/PUT/DELETE]
- body(string):Http的请求参数
- headers(Object):Http的请求头,默认为{}
语法结构:
fetch(url).then(fn2).then(fu3).then(fu3)....then(fn)fetch('/abc').then(data=>{return data.text();// fetch 特有的text,这里返回的是一个promise实例对象,用于获取后台的数据
}).hten(ret=>{// 注意! 这里得到的才是最终的数据console.log(ret)
})#配置选项 GET
fetch('/abc?id = 123'|rest风格:'/abc/123',{method:'get'
})
#rest接收
app.get('/abc/:id',(rqs,rps))#配置选项 POST
fetch('/abc?id = 123'|rest风格:'/abc/123',{method:'post',body:'name = list&pwd = 123',headers:{'Content-Type':'application/x-www-form-urlencoded'}
})#配置选项 POST ->JSON
fetch('/abc?id = 123'|rest风格:'/abc/123',{method:'post',body:JSON.stringify({name:'jack',pwd:'456'
}),headers:{'Content-Type':'application/json'}
})
axios
基于Promise用于浏览器和Node.js的Http客户端
特性
- 支持浏览器和Node.js
- 支持promise
- 能拦截请求和响应
- 自动转换JSON数据
响应结果的主要特性
- data: 实际响应回来的数据
- Headers:响应头信息
- Status:响应状态码
- statusText:响应状态信息
axios的全局配置
Axios.defaults.timeout = 3000; //设置超时
axios.defaults.baseURL = //默认地址
axios.defaults.headers[‘mytoken’] = ‘11asdasdklh’ // 设置请求头
axios.get('/adata').then(ret=>{// data属性名称是固定的,用于获取后台响应的数据console.log(ret.data)
})//配置请求头信息,对于跨域来讲,此操作需要配置该请求头信息。
axios.defaults.headers['mytoken'] = 'myToken';// 配置请求的基准URL地址
axios.defaults.baseURL = ’http://localhost:8090‘;// 会自动拼接该baseUrl
axios.get('axios-json').then(function(ret){console.log(ret.data)
})
常用API
1.GET传递参数
- 通过URL传递参数
- 通过params选项传递参数
#通过url传参
axios.get('/adate?id=123').then(ret=>{console.log(ret.data)
})
#通过params传参
axios.get('/adate',{//对象params:{id:789}
}).then(function(ret){console.log(ret.data)
})#响应请求
app.get('/adata',(req.res)=>{res.send(req.query.id)
}
#result请求
app.get('adata/:id',(req,res)=>res.send(req.params.id)
)
2.DELETE传递参数
#通过url传参
axios.delete('/adate?id=123').then(ret=>{console.log(ret.data)
})
#params传参
axios.delete('/adate',{//对象params:{id:789}
}).then(function(ret){console.log(ret.data)
})#响应请求
app.delete('/adata',(req.res)=>{res.send(req.query.id)
}
3.POST参数传递
通过选项传递参数(默认传递的是json格式数据)
axios.post('/adata',{uname:'tom',pwd:123
}).then(ret=>{console.log(ret.data)
})#响应请求
app.post('/adata',(req.res)=>{res.send(req.body.uname+'-----'req.body.pwd)
}
表单形式参数
通过URLSearchParams传递参数(application/x-www.form-urlencoded)
// 创建实例
const params = new URLSearchParams();
// 添加参数
params.append('key1',value1);
params.append('key2',value2);
// 将params放入请求中并发送请求
axios.post('/api/test',params).then(ret=>{console.log(ret.data)
})
4.PUT请求传参
// JSON形式
axios.put('/adata',{uname:'tom',pwd:123
}).then(ret=>{console.log(ret.data)
})#响应请求
app.put('/adata',(req.res)=>{res.send(req.body.uname+'-----'req.body.pwd)
}// 表单形式->创建实例
const params = new URLSearchParams();
// 添加参数
params.append('key1',value1);
params.append('key2',value2);
// 将params放入请求中并发送请求
axios.put('/api/test',params).then(ret=>{console.log(ret.data)
})
axios拦截器
请求拦截器
axios->请求拦截器->客户端
axios.interceptors.request.use(function(config){console.log(config.url)config.headers.mytoken = 'myToken';// 需要return出去,不然拦截器配置失效return config},function(err){console.log(err)})axios.get('url').then(function(data){console.log(data)
})
响应拦截器
在获取数据之前对数据做一些加工处理
服务器->响应拦截器->axios
axios.interceptors.response.use(function(res){console.log(res)// 这里直接拿到了data数据var data = res.data;// 返回给axiosreturn data},function(err){console.log(err)})axios.get('url').then(function(data){// 打印出来的是后台传递的数据console.log(data)
})
async
- asunc/await 是ES7引入的新语,可以更加方便的进行异步操作
- async 关键字用于函数上(async函数的返回值是Promise实例对象)
- await 关键字用于async函数当中(await可以得到异步的结果)
// 返回值promise对象
async function querData(id){const ret = await axios.get('/data');return ret;
}
querData().then(function(data){console.log(data)
})
aysnc/await处理多个异步请求
aysnc function queryData(){var info = await axios.get('async1');var ret = await axios.get('async2?info'+info.data)return ret.data
}
quertData().then(function(data){console.log(data)
})
Router路由
1.后端路由
- 概念:根据不同的用户URL请求,返回不同的内容
- 本质:URL 请求地址与服务器资源之间的对应关系
2.前端路由
- 概念:根据不同的用户事件,显示不同的页面内容
- 本质:用户事件与事件处理函数之间的对对那个欢喜
Vue Router 包含功能
- 支持HTML5历史模式或hash模式
- 支持嵌套路由
- 支持路由参数
- 支持编程式路由
- 支持命名理由
使用步骤:
// router-link是 vue 中提供的标签,默认会被渲染为a标签
// to 属性默认会被渲染为 href 属性
// to 属性的值会被默认渲染为 # 开头的 hash地址
// 1. 创建路由
<router-link to = '/register'>Register</router-link>
<router-link to = '/user'>User</router-link>// 2.添加路由占位符
<router-view></router-view>// 3.定义路由组件
const User = {template:'<h1>User组件</h1>'
}
const Register = {template:'<h1>Register</h1>'
}
// 4. 配置路由规则并创建路由实例var router = new VueRouter({// routes 是路由规则数组routes:[// 每个路由规则都是一个配置对象,其中至少包含path 和component两个属性// path:表示当前路由规则匹配的 hash 地址// component:表示当前路由规则对应要展示的组件{path:'/user',component:User},{path:'/register',conponent:Register}]})// 5.把路由挂载到Vue根实例中const vm = new Vue({el:'#app',data:{},// 挂载路由实例对象// 在ES6 中属性名跟变量相同可以直接写上变量值// router:routerrouterrouter})
路由重定向
用户在访问地址 A 的时候,强制用户跳转到 地址 c,从而展示特定的组件页面。
通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便的设置路由的重定向
var router = new VueRouter({routes:[// 其中,path 表示需要被重定向的 原地址,redirect 表示被重定向到哪里{path:'/',redirect:'/user'},{path:'/user',component:User},{path:'/register',conponent:Register}]
})
路由嵌套
- 点击父级路由链接显示模板内容
- 模板内容中又有子级路由链接
- 点击子级路由链接显示子级模板内容[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XFEXc8k8-1596001043681)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-28 上午10.55.46.png)]
const Register = {template:`<div><h1>register</h1>// 子组件路由<router-link to = "/register/tab1">tab1</router-link><router-link to = "/register/tab2">tab2</router-link>// 子组件路由占位符<router-view></router-view></div>`}const router = new VueRouter({routes:[{path:'/',redirect:'/user'},{path:'/user',component:User},// 构建关系{path:'/register',component:Register,// children[]子规则children:[{path:'/register/tab1',component:Tab1},{path:'/register/tab2',component:Tab2}]},]})
路由动态匹配
基本用法
$route与对应路由形成高度耦合,不够灵活,所以可以使用props将组件和路由解耦
<router-link to="/user/1">User1</router-link>
<router-link to="/user/2">User2</router-link>
<router-link to="/user/3">User3</router-link>var router = new VueRouter({routes:[// 动态路径参数,以冒号开头{path:'/user/:id',component:User}]
})const User = {template:'<div>// 路由组件中通过$route.params获取路由参数User{{$route.params.id}}</div>'
}
props的值为布尔类型
const router = new VueRouter({routes:[//如果 props 被设置为 true ,route.params 将会被设置为组件属性{path:'/user/:id',component:User,props:true}]
})
const User = {props:['id'], //使用props接受路由参数template:'// 使用路由参数<div>{{id}}</div>'
}# 接受一个对象时
const router = new VueRouter({routes:[//如果 props 被设置为 true ,route.params 将会被设置为组件属性{path:'/user/:id',component:User,props:{uname:'jack',age:23}}]
})
const User = {// 此时再想去接受id 已经不能访问了,因为props 已经是一个对象了,Props中有什么,才能取什么!props:['uname','age'], //使用props接受路由参数template:'// 使用路由参数<div>{{id}}</div>'
}# 接受一个对象,并拿到id
const router = new VueRouter({routes:[//如果 props 被设置为 true ,route.params 将会被设置为组件属性{path:'/user/:id',component:User,// roite->动态参数对象 props:route => ({uname:'jack',age:20,id:route.params.id})}}]
})
const User = {props:['uname','age','id'], //使用props接受路由参数template:'// 使用路由参数<div>{{id+'-'+uname+'-'+age}}</div>'
}
路由命名
为了更加方便的表示路由的路径,可以给路径规则起个别名
// name:路由别名 params:需要的参数
<router-link :to="{name :'user',params:{id:3}}"></router-link>const router = new VueRouter({routes:[{// 该路由别名name:'user',path:'/user/:id',component:User}]
})
页面导航的两种方式
- 声明式导航:通过点击链接实现导航的方式
- 链接 或vue中的
- 编程式导航:通过调用JavaScript形式的API实现导航的方式,叫做编程式导航
- 例如:普通网页中的location.href
编程式导航基本用法
- this.$router.push(‘hash地址’)
- this.$router.go(n) // 历史记录的前进或后退
// 定义路由组件const User = {template:`<div><button @click = 'goRegister'>去注册</button></div>`,methods: {goRegister:function(){// 完成导航功能this.$router.push('/register')}},}const Register = {template:`<div><h1>register</h1><button @click = 'goBack'>回到上一页</button></div>`,methods: {goBack:function(){this.$router.go(-1)}},}
后台管理实例
<script>// 定义 APP 根组件const App = {template: `<div><!-- 头部区域 --><header class="header">传智后台管理系统</header><!-- 中间主体区域 --><div class="main"><!-- 左侧菜单栏 --><div class="content left"><ul><li><router-link to="/users">用户管理</router-link></li><li><router-link to="/rights">权限管理</router-link></li><li><router-link to="/goods">商品管理</router-link></li><li><router-link to="/orders">订单管理</router-link></li><li><router-link to="/settings">系统设置</router-link></li></ul></div><!-- 右侧内容区域 --><div class="content right"><div class="main-content"><router-view /></div></div></div><!-- 尾部区域 --><footer class="footer">版权信息</footer></div>`}const Users = {data() {return {userlist: [{ id: 1, name: '张三', age: 10 },{ id: 2, name: '李四', age: 20 },{ id: 3, name: '王五', age: 30 },{ id: 4, name: '赵六', age: 40 }]}},methods: {goDetail(id) {console.log(id)this.$router.push('/userinfo/' + id)}},template: `<div><h3>用户管理区域</h3><table><thead><tr><th>编号</th><th>姓名</th><th>年龄</th><th>操作</th></tr></thead><tbody><tr v-for="item in userlist" :key="item.id"><td>{{item.id}}</td><td>{{item.name}}</td><td>{{item.age}}</td><td><a href="javascript:;" @click="goDetail(item.id)">详情</a></td></tr></tbody></table></div>`}const UserInfo = {props: ['id'],template: `<div><h5>用户详情页 --- 用户Id为:{{id}}</h5><button @click="goback()">后退</button></div>`,methods: {goback() {// 实现后退功能this.$router.go(-1)}}}const Rights = {template: `<div><h3>权限管理区域</h3></div>`}const Goods = {template: `<div><h3>商品管理区域</h3></div>`}const Orders = {template: `<div><h3>订单管理区域</h3></div>`}const Settings = {template: `<div><h3>系统设置区域</h3></div>`}// 创建路由对象const router = new VueRouter({routes: [{path: '/',component: App,redirect: '/users',children: [{ path: '/users', component: Users },{ path: '/userinfo/:id', component: UserInfo, props: true },{ path: '/rights', component: Rights },{ path: '/goods', component: Goods },{ path: '/orders', component: Orders },{ path: '/settings', component: Settings }]}]})const vm = new Vue({el: '#app',router})</script>
前端工程化
模块化相关规范
- 模块化,就是把单独的一个功能封装到一个模块(文件中),模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块
- 模块化开发的好处:方便代码复用,提高开发效率,便于维护
服务器端模块化规范
CommonJS
- 模块分为 单文件模块 与 包
- 模块成员导出: module.exports 和exports
- 模块成员导入:require(‘模块标识符’)
ES6模块化规范
ES6模块化规范,是浏览器端与服务器端通用的模块化开发规范。
- 每个 js 文件都是一个独立的模块
- 导入模块成员使用 import 关键字
- 暴露模块成员使用 export 关键字
默认导出
- 默认导出语法 export default 默认导出的成员
- 在每个模块中只能使用一次export default 否则将会报错
// 当前文件模块为 m1.js
let a = 10;
let c = 20;
let d = 30
function show();
// 将本模块中的私有成员暴露出去,供其他模块使用
export default{a,c,show
}
默认导入
- 默认导入语法 import 接受名称 from ‘模块标识符’
// 导入模块成员
import m1 form './m1.js'
console.log(m1)
// 打印输出结果为:
{a:10,c:20,show:[function:show]}
按需导出
- 按需导出语法 export let s1 = 10
// 按需导出
export let r = 10;
export let g = 20;
export function say(){console.log(sayFunction)
};
按需导入
- 按需导入语法 import {s1} from ‘模块标识符’
// 如果想按需导出跟默认导出同时出现 需要使用',{}' 可以使用as 给该属性起别名
import m1,{r,g as gg,say}from './m1.js'// console.log(m1)
console.log(say)
直接导入并执行模块代码
有时候,我们只想单纯执行某个模块中的代码,并不需要得到模块中向外暴露的成员,
此时,可以直接导入并执行模块代码。
// m2模板中的内容
for(let i = 0;i<3;i++){console.log(i)
}
// index.js 中导入
import './m2.js'//控制台输出结果123
webpack
概述:
一个流行的前端项目构建工具(打包工具),webpack提供了友好的模块化支持,以及代码压缩混淆,处理js兼容问题、性能优化等强大功能,提高开发效率和项目的可维护性。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-506vQ6LR-1596001043683)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-28 下午8.58.14.png)]
使用
->创建列表隔行变色项目
- 新建项目空白目录,并运行 npm init -y 命令,初始化包管理配置文件 package.json
- 新建 src 源代码目录
- 新建 src -> index.html 首页
- 初始化首页基本的结构
- 运行 npm install jquery -s 命令,安装jQuery
- 通过模块化的形式,实现列表隔行变色效果
此处有ES6兼容性问题,需要配合webpack使用。
- 在项目根目录中,创建名为webpack.config.js 的webpack配置文件
1.webpack配置文件:
module.exports = {mode:'development' // mode 用来指定构建模式,development该值为开发模式,代码不会混淆、压缩
}
2.在package.json 配置文件中的scripts节点下,新增dev 脚本如下
"scripts":{"dev":"webpack" // script 节点下的脚本,可以通过npm run执行
}
3.在终端中运行 npm run dev 命令,启动 webpack 进行项目打包。
配置打包的入口与出口
- 打包的入口文件为 scr -> index.js
- 打包的输出文件问 dist -> main.js
如果需要修改打包的入口与出口,可以在 webpack.config.js 中新增如下配置信息:
const path = require('path') // 导入 node.js中专门操作路径模块
module.exports = {entry:path.join(_dirname,'./src/index.js'), // 打包入口文件位置output:{path:path.join(_dirname,'./dist'),// 输出文件的存放路径filename:'bundle.js' // 输出文件的名称}
}
配置自动webpack打包功能
- 运行 npm install webpack-dev-server -D命令,安装支持项目自动打包的工具
- 修改 package.json ->scripts 节点中的dev命令如下
"scripts":{"dev":"webpack-dev-server" // script 节点下的脚本,可以通过 npm run dev执行
}
- 将 src -> index.html 中,script 脚本的引用路径,修改为’bundle.js’
- 运行 npm run dev 命令重新打包
- 访问 http://localhost:8080 查看自动打包效果
- 在文件夹中生成的bundle.js 文件运行在内存中
- 注意:
- webpack-dev-server 会启动一个实时打包的 http 服务器
- webpack-dev-server 打包生成的输出文件,默认放到项目根目录中,并隐藏。
- 注意:
配置生成预览页面
- npm install html-webpack-plugin -D 下载插件
- 修改 webpack.config.js 文件头部区域,添加如下配置:
// 导入生成预览页面的插件,得到一个 构造函数
const HtmlWebpackPlugin = require('html-web-plugin');
const htmlPlugin = new HtmlWebpackPlugin({template:'./src/index.html',//指定要用到的模板文件filename:'index.html' // 指定生成的文件的名称,该文件存在于内存中,在项目目录中不现实
})
- 修改webpack.config.js文件中对外暴露的配置对象,新增如下配置节点
module.exports = {plugins:[htmlPlugin]// plugins 数组是 webpack 打包期间会用到的一些插件列表
}
打包完成自动打开项目
配置参数
//package.json 中配置
// --open 打包完成自动打开浏览器页面
// --host 配置 Ip 地址
// --port 配置端口
"scripts":{"dev":"webpack-dev-server --open --host 127.0.0.1 --port 8088"
}
通过loader 打包非js模块
在实际开过程中,webpack 默认只能打包处理以 .js 后缀名的模块,其他非 .js 后缀名结尾的模块,webpack默认处理不了,需要调用 loader 加载器才可以正常打包,否则会报错。
loader 加载器可以协助 webpack 打包处理特定的文件模块,比如:
- less-loader 可以打包处理 .less 相关的文件
- sass-loader 可以打包处理 .scss 相关的文件
- url-loader 可以打包处理 css 中与 url路径相关的文件[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uDNB1R4C-1596001043684)(/Users/limboaaa/Library/Application Support/typora-user-images/截屏2020-07-29 上午10.41.54.png)]
打包处理css文件
- npm i style-loader css-loader -D 安装处理css文件的 loader
- 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
# webpack.config.js
// 所有第三方文件模块的匹配规则module:{rules:[{test:/\.css$/,use:['style-loader','css-loader']}]}
# test 表示匹配的文件配型, true 表示对应要调用的loader
# use 数组中指定的 loader 顺序是固定的
# 多个 loader 的调用顺序是:从后往前调用
打包处理less 文件
- npm i less-loader less -D 安装处理 less 文件的 loader
- 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
# webpack.config.js
// 所有第三方文件模块的匹配规则module:{rules:[{test:/\.less/,use:['style-loader','css-loader','less-loader']}]}
打包处理scss 文件
- npm i sass-loader node-sass -D 安装处理 sass 文件的 loader
- 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
# webpack.config.js
// 所有第三方文件模块的匹配规则module:{rules:[{test:/\.scss/,use:['style-loader','css-loader','sass-loader']}]}
配置postCSS 自动添加css的兼容前缀
- 运行 npm i postcss-loader autoprefixer -D命令
- 在项目根目录中创建 postcss 的配置文件 postcss.config.js并初始化如下配置
const autoprefixer = require('autoprefixer') // 导入自动添加前缀插件
module.exports = {plugins:[autoprefixer] //挂载插件
}
3.在 webpack.config.js 的 module->rules 数组中,修改 css 的loader规则如下
module:{rules:[{test:/\.css$/,use:['style-loader','css-loader','postcss-loader']}]
}
打包处理图片 文件字体文件
- npm i url-loader file-loader -D
- 在 webpack.config.js 的 module ->rules 数组中,添加 loader规则如下
module:{rules:[{test:/\.jpg|png|git|bmp|ttf|eot|svg|woff|woff2$/,use:'url-loader?limit=16940' // 图片大小 单位字节(byte)}]
}
打包处理js文件的高级语法
- 安装babel 转换器相关的包:npm i babel-loader @babel/core @babel/runtime -D
- 安装babel 语法插件相关的包:npm i @babel/preset-env @babel/plugin-transform-runtime @babel/plugin-proposal-class-properties -D
- 在项目跟目录中,创建babel配置文件babel.config.js 并初始化如下配置:
module.exports = {presets:['@babel/preset-env'],plugins:['@babel/plugin-transform-runtime','@babel/plugin-proposal-class-properties'] //挂载插件
}
4.在 webpack.config.js 的module -> rules 数组中,添加 loader 规则如下:
// exclude 为排除项 表示babel-loader 不需要处理:node_modules中的js文件
{test:/\.js$/,use:'babel-loader',exclude:/node_modules/}
Vue 单文件组件
传统组件的问题和解决方案
- 全局定义的组件必须保证组件的名称不重复
- 字符串模板缺乏语法高亮
- 不支持CSS意味着当HTML和JavaScript 组件化时,CSS明显会被遗漏。
- 没有构建步骤限制,只能使用HTML和ES5 JavaScript,而不能使用预处理器(如:babel)
vue进阶之路 webpack打包 持续更新相关推荐
- 软件测试进阶之路 - 目录 (持续更新)
1. 软件测试基础 1-1 计算机基础 1-1.1 近代计算机的发展史 1-1.2 计算机分代 1-1.3 计算机分类 1-1.4 计算机的组成 1-1.5 操作系统基础 1-1.6 Windows ...
- 【我的OpenGL学习进阶之旅】【持续更新】关于学习OpenGL的一些资料
目录 一.相关书籍 OpenGL 方面 C方面 NDK 线性代数 二.相关博客 2.0 一些比较官方的链接 2.1 OpenGL着色器语言相关 2.2 [[yfan]](https://segment ...
- 一幅长文细学Vue(一)——Webpack打包工具
1 项目开发工具 摘要 在本文中,我们会详细讨论webpack是如何打包发布项目,不过对于Vue来说,Vite可以做到和webpack一样的功能. 声明:如果想要看懂此文章,需具备node.js中 ...
- webpack文章(持续更新)
webpack文章: http://webpackdoc.com/install.html webpack打包:http://webpackdoc.com/ (先把文章看一篇,然后照着模仿一遍) ht ...
- 学习VUE时,利用webpack打包的错误处理方法
今天看哔哩哔哩Vue的课程时候遇到三个问题记录一下 1.安装webpack npm install webpack -g 新版需要同时安装 2.安装webpack-cli npm install -- ...
- 前端(以Vue为例)webpack打包后dist文件包如何部署到django后台中
由于现在前端使用的三大框架配合webpack可以实现快速打包,为部署到服务端提供了非常大的便利,那么在前端打包后,应该做些什么可以部署到django的后台中呢? 1.打包后文件包dist 进入到 di ...
- 前端自学Vue笔记干货(第一版,持续更新中~~~)
学习笔记 Vue笔记 nprogress使用 npm i nprogress -S 基本上都是在对axios进行二次封装.前置守卫路由或者封装成工具函数的.js文件中用到 import nprogre ...
- C++_Leetcode刷题之路——简单(持续更新)
目录 1. 两数之和 解一: 解二: 7. 整数反转 解一: 9. 回文数 解一: 13. 罗马数字转整数 解一: 14. 最长公共前缀 解一: 20. 有效的括号 解一: 21. 合并两个有序链表 ...
- vue+elementUI项目的踩坑~~持续更新
日常工作记录,如果对你有帮助请点亮小~心~心喔~~~ 接受礼貌的批评指导 目录: 1.el-form只有一个搜索条件时页面刷新问题: 2.el-tree树形控件,给项目安装jsx语法: 3.el-ca ...
最新文章
- linux mint 相关环境配置
- python3 读取.plist文件_Python学习笔记 -5 - 文件操作
- [trustzone]-ARM trustzone技术下常见的软件框图
- android classloader的功能和工作模式,Android中ClassLoader和java中ClassLoader有什么关系和不同...
- 组合数据类型练习、英语词频统计
- mysql连接不上怎么重置密码错误_MySQL数据库连接不上、密码修改问题
- 前端图片有时候能显示有时候不显示_web前端基础教程:两种数据存储思路
- Python使用装饰器捕获异常
- Leetcode 64. 最小路径和 -- DP算法
- 查找算法-------插值查找
- python判断字符类型例题_Python面试题:字符类型的考察
- 软考中级软件设计师该怎么备考
- MMDetection CenterNet 源码解析
- QWebEngineView崩溃及替代方案
- jquery的图片播放插件 - colorbox
- 晕LIVEnbsp;WRITER设置教程是网易…
- 脑机接口技术使瘫痪病人重获运动能力,4D打印技术已经到来!|技术前沿洞察...
- TC358775XBG是一颗将MIPI DSI信号转换成single/ dual -link LVDS的芯片,最高分辨率支持到1920x1200
- “人工智能”•从入门到入土 –导言
- 因果6-估计因果效应