框架,framework,是能够让程序开发人员更好的专注于业务逻辑的开发,而无需关心底层功能的实现
vue是一个渐进式JavaScript框架
国人自己开发的框架,作者是尤雨溪
官网:cn.vuejs.org
入门简单
手册详细
生态圈活跃
vue两大核心:数据驱动页面、组件化
vue、vue-cli脚手架、vue-router路由、ui库、样式预处理器stylus、网络请求axios、状态管理vuex、服务器端渲染
(1)易学
(2)速度快
(3)采用虚拟DOM
(4)指令系统
(5)生态圈活跃
1.兼容性,不支持ie8及以下的浏览器
2.语法报错提示不是特别的准确
直接引用js文件
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>结合node环境,直接下载
(1)先配置淘宝镜像
(2)下载vue
npm i vue(1)el配置选项
指定vue的作用范围,相当于js中的queryselector,只会匹配到满足条件的第一个标签,所以我们一般使用id选择器,不使用class或者标签选择器
(2)data配置选项
初始化页面数据,初始化的数据会直接挂载到vue实例上,直接使用key属性,作为变量名
(3)自定义函数
methods:用来存放用户自定义函数
(1)mustache语法(文本插值):{{ 变量名或单行js语法 }}
<!-- 1.引入vue.js核心文件--> <script src="../../node_modules/vue/dist/vue.js"></script> <body> <!-- 2.设置挂载点 --> <div id="app"> <!-- <h2>小豪,{{txt}}</h2> --> <h2>{{'小豪,'+txt}}</h2> <p>{{100*30}}</p> <p>{{isshow?'显示':'不显示'}}</p> //获取对象类型的属性的时候,只需要对象.属性名 <p>{{'姓名:'+user.name}}</p> //数组类型:数组名[下标].属性名 <p>{{users[1].name}}</p> <!-- vue框架是一个声明式的框架,所以在挂载点内使用变量的时候,需要先声明 --> <p>{{name}}</p> </div> <script> // 3.实例化vue let vm = new Vue({ el: '#app',//相当于element,querySelector() data: { txt: 'vue基础学习', isshow: false, user: { name: '小明', age: 18 }, users: [ { name: '小明', age: 18 }, { name: '小li', age: 19 } ] } }); </script> </body> </html>所有v-xxx指令都要写在标签的开始标签上
2.v-text
可以解析data中设置的初始值,v-text把初始值设置到指定的标签内
和mustache的区别:
如果要展示的内容是固定的,可以使用v-text
如果要展示的内容中的一部分是固定的,就要使用mustache语法
3.v-html
可以解析带有html标签的内容
1.v-if
根据条件表达式或者布尔的结果展示或者不展示指定标签
当表达式的结果为true时,在页面结构中才会出现指定的标签
2.v-show
不论条件表达式和boolen值的结果是什么,v-show指定的标签都会在页面结构中存在
当表达式的结果为true时,在页面结构中会显示指定的标签
当表达式的结果为false时,在指定的标签上会添加按一个display:none属性
使用场景:
在页面中,要根据指定的内容来频繁的切换某些内容时,要使用v-show
v-for
<标签 v-for="每次要遍历的变量名[,每次遍历的元素在数组中的下标] of/in 要遍历的数组"></标签>可以根据数组元素数量来生成指定数量的标签
v-for循环 key属性
遍历的数据源类型是数组时,第一个参数是数组中每一项元素,第二个参数是数组中每项元素的下标
遍历的数据源类型是对象时,第一个参数是对象中每一项元素的value属性,第二个参数是对象中每项元素的key属性,第三个参数是每项元素的下标
v-on
语法格式
<标签 v-on:事件名=“自定义函数名”></标签>可以简写
<标签 @事件名=‘自定义函数’></标签>选项卡小练习
<div id="app"> <button v-for="(btn,btninx) of btns" @click="showidx=btninx">{{btn}}</button> <div class="content" v-for="(btn,index) of btns" v-show="index ==showidx"> <p v-for="con of news[index]">{{con}}</p> </div> </div> <script> new Vue({ el:'#app', data:{ showidx:0, btns:['bj','zg','sj'], news:[ { '新闻1':'新闻1111', '新闻2':'2', '新闻3':'3', }, { '新闻4':'1', '新闻5':'2', '新闻6':'3', }, { '新闻7':'44444', '新闻8':'2', '新闻9':'3', } ] } }) </script>v-bind
语法格式:
<标签 v-bind:属性名=“属性值”></标签>可以简写成
<标签 :属性名=“属性值”></标签>1.style
(1)第一种用法:直接使用变量
<标签 v-bind:style="styleA"></标签>(2)第二种用法:使用对象
<标签 v-bind:style="{属性名:属性值,...}"></标签>如果属性名中包含’-’,把横杠去掉,并把横杠后的字母变成驼峰法命名,或者把属性名用’'引起来
如果属性值是一个单词,比如red,也要使用’'引起来
比如,font-size变成fontSize
(3)第三种用法:使用数组
<标签 v-bind:style="[变量1,变量2]"></标签> <p :style="[ colorA,fontA]">123333</p> <script> new Vue({ el:'#app', data:{ colorA:{ color:'pink' }, fontA:{ fontSize:'40px' } } }) </script>2.class
第一种用法:直接使用变量
<标签 v-bind:class="styleA"></标签>(2)第二种用法:使用对象
<标签 v-bind:class="类名:表达式或者布尔值"></标签>当表达式或Boolen值的结果为true时,表示使用该类名,否则就不使用该类名
(3)第三种用法:使用数组
<标签 v-bind:class="['类名1','类名2']"></标签>class使用数组时,每一个类名都要加上引号才会被解析
设计模式
MVC和MVVM
MVC:强制的把程序的输入,输出和处理分开,方便维护
Model数据模型层
View:视图层
Controller控制器层 核心
MVVM:
Model数据模型层
View视图层
ViewModel视图模型层
1.内容展示
(1)输入框
<input type="text" v-model="msg"> <p>{{msg}}</p> <script> new Vue({ el:'#app', data:{// 可以理解为model msg:'呵呵呵' } }) </script>(2)文本域
<textarea v-model="article"></textarea> <p>{{article}}</p> </div> <script> new Vue({ el:'#app', data:{// 可以理解为model msg:'呵呵呵', article:'wenzhang' } }) </script>(3)checkbox
<input type="checkbox" v-model="变量名" value="属性值">实例代码
<input type="checkbox" v-model="hobbies" value="1">看电影 <input type="checkbox" v-model="hobbies" value="2">打游戏 <input type="checkbox" v-model="hobbies" value="3">吃饭 <p>{{hobbies}}</p> <script> new Vue({ el:'#app', data:{// 可以理解为model msg:'呵呵呵', article:'wenzhang', hobbies:[] } }) </script>布尔值
(4)radio
radio和checkbox在使用v-model一定要加上value值,
<input type="radio" v-model="sex" value="男">男 <input type="radio" v-model="sex" value="女">女(5)select
<select v-model="sel"> <option value="请选择">请选择</option> <option value="web" >web</option> <option value="java" >java</option> <option value="ui">ui</option> </select>value属性是选中之后展示的值
vue支持我们自己定义一些指令来完成一定的操作。
语法格式:
Vue.directive('指令名称',{ inserted:function(el){//inserted当指令插入到标签中时,自动执行 el.focus();//让元素获得焦点 } });注意:在Vue实例化之前设置自定义指令
bootstrap
当用户输入完姓名、年龄等信息后,点击提交按钮完成数据的添加操作
数据添加完成后,在表格中显示已添加的用户信息
示例代码
<div id="app"> <form class="form-horizontal "> <h3 class="text-center">用户信息收集</h3> <div class="form-group row"> <label for="user" class="control-label col-sm-2">姓名</label> <div class="col-sm-6"> <input type="text" id="user" class="form-control" autocomplete="off" v-model="info.user"> </div> </div> <div class="form-group row"> <label for="age" class="control-label col-sm-2">年龄</label> <div class="col-sm-6"> <input type="text" id="age" class="form-control" autocomplete="off" v-model="info.age"> </div> </div> <div class="form-group row"> <label class="control-label col-sm-2"></label> <button type="button" class="btn btn-primary" @click="add">提交</button> <button type="reset" class="btn" @click="clear">重置</button> </div> </form> <table class="table table-hover table-bordered"> <thead> <tr> <th>姓名</th> <th>年龄</th> <th>操作</th> </tr> </thead> <tbody > <tr v-for="item of users"> <td>{{item.user}}</td> <td>{{item.age}}</td> <td> <button class="btn">编辑</button> <button class="btn btn-danger">删除</button> </td> </tr> <tr v-if="users.length ==0"> <td colspan="3">尚无用户信息</td> </tr> </tbody> </table> </div> <script> new Vue({ el: '#app', data: { info: { user: '', age: '' }, users: [] }, methods: { add() { this.users.push(JSON.parse(JSON.stringify(this.info))); this.reset(); }, clear() { this.reset(); }, reset() { this.info = { user: '', age: '' } } } }) </script>(1)阻止默认事件
<标签 @事件名.prevent></标签>(2)阻止事件冒泡
<标签 @事件名.stop></标签>(3)捕获事件
<标签 @事件名.capture></标签>不会阻止或改变事件冒泡,但是会改变冒泡函数的执行顺序,哪个标签上有capture哪个标签优先执行
(4)自身
<标签 @事件名.self></标签>.self强调的是当前操作的元素只有是她本身时,才会触发指定的函数,类似阻止事件冒泡
(5)只执行一次
<标签 @事件名.one></标签>(1)懒加载 lazy
不再对数据进行实时双向绑定,而是在执行change事件时,才进行双向绑定
(2)number
不强制页面用户输入的内容,而是转变数据类型为number
如果写的内容是数字开头,字符串结尾,那么number修饰符会把字符串过滤掉
如果是字符串开头,不做任何修改
(3)trim
过滤输入内容左右两边的空格,不包含中间的空格
1.enter
2.left
3.right
4.middle
5.esc
6.tab
7.down
8.up
1.LocalStorage 只要不人为删除掉数据,数据就一直存在
2.sessionStorage 只要关闭浏览器,数据就消失了
setItem(‘key’,value)
getItem(‘key’)
removeItem(‘key’)
根据指定的key删除
clear()
删除本次存储中的所有数据
1.基本步骤
2.示例代码
<div id="root"> <input type="text" v-model="ipt" @keydown.down="down" @keydown.up="up" @keydown.enter="enter"> <button @click = serach>搜索</button> <ul> <li v-for="(item,index) of arr" v-if="4>index" :class="{sel:index==idx}">{{item}}</li> </ul> </div> <script > let vm=new Vue({ el:'#root', data: { arr:[], ipt:'', idx:-1 }, methods: { serach(){ // console.log() var s = document.createElement("script"); // cb会点函数名称 wb要搜索的关键词 s.src="http://suggestion.baidu.com/su?cb=getwd&wd="+this.ipt; document.body.append(s); }, down(e){ this.idx++; if(this.idx>3){ this.idx = -1; } }, up(){ this.idx--; if(this.idx<0){ this.idx = 4; } }, enter(){ if(this.idx==-1){ return; }else{ // this.ipt =this.arr[this.i] window.open('https://www.baidu.com/s?wd='+this.arr[this.idx]) } } } }) function getwd(res){ vm.arr = res.s; // console.log(res); } </script>作用:可以对页面中已经定义好的变量进行监听,一旦变量值发生了改变,就可以执行一定的操作。
语法格式:
语法1:
new Vue({ el data methods watch:{ 变量名(){ 函数 } } })语法二:
new Vue({ el data methods watch:{ 要监听的变量名:{ handler(newVal, oldVal) {} } })如果要监听的变量类型为对象或数组时,普通监听无法实现监听的变化效果,需要使用深度监听
new Vue({ el data methods watch:{ 要监听的变量名:{ handler(newVal, oldVal) {}, deep:true//进行深度监听 } })作用:如果页面上有需要频繁进行数学运算之后得到结果内容时,可以使用计算属性来实现
页面上可以不用在data中定义计算结果的初始变量,只要在挂载点内使用计算属性的结果后,计算结果对应的函数会自动触发
计算属性关联的数据,一旦发生改变,那么计算属性的逻辑函数会重新执行
语法格式
new Vue({ el data methods, computed:{ 要计算的结果(){ 业务逻辑... return 结果; } } })在计算属性中内置了两个方法,一个是get,用来读取数据,一个是set用来设置数据。
在vue中默认使用的是get方法,只要在页面中使用了计算属性的变量或者计算属性变量依赖的数据发生了改变时,get方法会重新执行。
示例代码:
<div id="app"> {{ sum }} </div> <script> let vm = new Vue({ el:"#app", data:{ num1:10, num2:20 }, computed:{ sum:{ get(){ console.log('get') return this.num1 + this.num2 }, set(){ console.log('set') } } } }) </script>只有给计算属性的变量直接赋值时,set函数才会执行。
可以在谷歌浏览器的控制台中进行测试
vm.sum = 100 //此时自动执行set函数 vm.num1 = 20 //此时自动执行get,因为计算属性依赖于num1(1)相同之处
都可以根据依赖的数据变化,自动的触发相应的逻辑函数代码
(2)不同之处
计算属性的逻辑函数在页面使用了计算属性的变量后,就会自动的触发对应的逻辑函数
监听是只有依赖的数据发生了变化的时候,才会触发对应的逻辑函数
(3)注意事项
如果计算属性和监听,都对相同的数据进行操作,那么就会产生冲突互相影响。
methods中定义的函数,只要在页面上调用,就会无条件执行
计算属性依赖的数据不发生变化时,只是读取,不会重新计算
计算属性特点:
依赖于数据,如果数据发生变化,那么计算属性就会自动更新
计算属性的结果无需在data中定义,在页面中可以直接使用
会在vue实例上产生一个缓存,如若依赖的数据不发生变化,就会读取缓存
作用:
对页面中要展示的数据进行处理,满足页面数据展示的需求。
定义:
使用:
需要通过管道符来使用定义好的过滤器,要进行过滤的源数据 | 过滤器名称
传递额外参数:
管道符左边的是过滤器中的第一个参数,在管道符右边过滤器名称处,可以通过小括号来传递额外的参数。
示例代码:
<div id="app"> <!-- 使用过滤器 --> 总价格:{{ totalPrice | formatPrice }} </div> <script> new Vue({ el:"#app", data:{ totalPrice:2999.9 }, filters:{ //定义一个格式化价格的过滤器 formatPrice(val,n=1){ return '¥'+val.toFixed(n)+'元'; } } }) </script>语法:
Vue.filter('过滤器名称',function(形参1[,形参N]){ ... }) new Vue({})全局定义的过滤器,可以在当前页面中的所有vue实例中来使用过滤器
transition标签
当元素通过v-if或者v-show或者动态组件的方式进行展示或者不展示的标签下就可以设置过渡动画
注意:想要有效果,就要用标签包裹
进入状态
v-enter 设置进入开始状态的样式
v-enter-active 设置进入进行中状态的样式
v-enter-to 设置进入结束的状态的样式
离开状态
v-leave 设置离开开始状态的样式
v-leave-active 设置离开进行中状态的样式
v-leave-to 设置离开结束的状态的样式
如果页面上有多个元素需要设置过渡动画,可以给添加name标签进行区分
<style> .page-enter{ left: 0px; } .page-enter-active{ transition: left 2s; } .page-enter-to{ left: 300px; } </style> <transition name="page" mode=""> <div class="page" v-show="isshow"></div> </transition>animate.css
1.可以用npm安装或者直接引入在线链接
2.给需要设置过渡动画的标签添加标签
3.给标签设置开始和结束的动画类名
<transition name="" mode="" leave-active-class="animate__animated animate__backOutUp" enter-active-class="animate__animated animate__backInDown" > <div id="box" v-show="isshow"></div> </transition>vue实例从创建、挂载、更新、销毁的一个完整的过程叫生命周期
(1)页面渲染期:页面加载时自动触发的钩子函数
beforeCreate 创建之前 vue实例创建之前
Created 创建完成 vue实例创建完成
beforeMount 挂载之前 vue实例创建完成,解析vue语法之前
Mounted 挂载完成 解析vue语法,在这儿发起网络请求
(2)页面更新期:页面上的数据有变化时自动触发的钩子函数
beforeUpdate 更新之前
updated 更新完成
(3)页面销毁期:vue实例被销毁时会自动触发的钩子函数 vm.$destroy ()
beforeDestroy 销毁之前
destroy 销毁完成
组件是vuejs中最强大的功能之一,组件可以扩展html元素,封装可复用的代码,在大型项目中,为了合理分工、代码复用。每一个组件都是一个vue的实例,所以vue实例中的配置选项在组件中都可以使用。
语法格式
new Vue({ el data ... components:{ "组件名称":{ template:'组件内容' } } })template是组件的模板内容,必须设置
使用:要在挂载点内,把组件名称当成一个普通的标签使用即可
关于组件名称注意事项:
1.组件名称不能是html内置的标签;
2.组件的名称不能是html内置的标签全大写
3.如果组件的名称包含大写字母(非首字母),在使用组件时,要把大写字母转换成:-小写字母
myDiv需要写成
关于组件模板注意事项:
组件的模板内容中只能有一个根标签
全局注册的组件,可以在所有的vue实例中使用
语法格式:
Vue.component('组件名称',{ template:'组件内容' }) new Vue({ })template标签不会被浏览器解析,并且还可以在其中编写html标签
组件中的template属性和template标签是两个不同的东西,不要混为一谈
鉴于组件的template属性中直接编写页面内容不是很方便,所以我们可以结合template标签来设置组件中的内容
但是需要把template属性和template标签关联起来
<div id="app"> <first></first> </div> <template id="first"> <div> <h1>first</h1> </div> </template> <script> new Vue({ el:'#app', components: { first:{ // 指定关联的template标签 template:'#first' } } }) </script>(2)代码优化
由于减的基本构成是一个对象,当页面中组件数量比较多时,注册组件时的代码量就比较多,看上去非常混乱,我们可以把组件的构成提前定义好
var first = { template:'#first', } var second = { template:'#second' } new Vue({ el:'#app', components: { first, second, } })方法一:直接注册使用
<template id="main"> <div> <my-left class="left"></my-left> <my-right class="right"></my-right> </div> </template> <script> let myLeft = { template:'#left' } let myRight = { template:'#right' } let myMain = { template:'#main', components:{ myLeft,myRight } } new Vue({ el: '#app', components: { myTop, myMain, myBottom } }) </script>方法二:使用extend方法
<div id="app"> <parent></parent> </div> var child = Vue.entend({ template:'<div>子组件</div>' }) var parent = Vue.extend({ template:'<div>父组件<child></child></div>', components:{ child } }) new Vue({ el: '#app', components: { parent } }) </script>因为对象类型的数据影响,组件复用时,数据会相互影响,所以为了保证数据的正确性和独立性,在组件中,定义data时,要写成一个函数并返回一个对象来定义组件的初始数据
示例代码:
版本的问题:
稳定版 2.9.6
最新版 4.X
基础环境:node
(1)webpack
npm i webpack -g(2)vue-cli脚手架
npm i vue-cli -g进入一个非系统盘的目录中
vue init webpack projectname注意:项目名称不能包含中文和空格
初始化项目步骤
(1)先执行初始化命令,回车
(2)Project name(myapp)?
确认项目名称,不改则回车
(3)Project description A Vue.js project
项目描述,不改则回车
(4) Author (林聪 admin@nimda)
项目作者,不改则回车
(5) Vue build (Use arrow keys) Runtime + Compiler: recommended for most users
项目运行模式,运行时编译
(6)Install vue-router? (Y/n)
是否安装路由,暂时不安装,n
(7)Use ESLint to lint your code? No
是否使用 ESLint 验证代码格式
(8) Set up unit tests (Y/n) n
是否创建单元测试
(9)Setup e2e tests with Nightwatch? (Y/n) n
是否要使用端对端测试
(10) Should we run npm install for you after the project has been created? (recommended) (Use arrow keys)
Yes, use NPM
进入项目根目录下,执行
npm run devbuild 项目打包依赖的目录
config 项目配置文件目录
node_modules 项目依赖目录
project 项目跟目录
src 项目源代码
assets 项目静态资源目录
components 存放项目自定义组件
App.vue 项目跟组件
main.js 项目启动文件
static 存放通过域名直接访问的静态资源
.bablerc es6语法解析配置
.editorconfig 编辑器配置文件
.gitignore git 忽略的配置文件
.postcssrc.js postcss的配置文件
index.html 项目首页
package-lock.json
package.json 项目依赖配置文件
项目执行顺序:
index.html
/src/main.js
/src/App.vue
/src/components/HelloWorld.vue
(1)模板template 必须
(2)js script
(3)css style
通过自定义属性和props来实现
(1)在父组件中使用子组件时,通过自定义属性来传递数据
<v-child imgUrl ="" title="学习群" desc="组团学习高效"></v-child>(2)在子组件中通过props来接收父组件中传递过来的数据
<script> export default { props: ['imgUrl','title','desc'] } </script>props验证
(1)验证数据类型,只是在控制台中给出警告,不会影响程序的运行
在子组件中接收数据时,props写成一个对象
props:{ 要接收的参数名:数据类型 写法一:仅验证数据类型 gift:String, 写法二:不仅仅验证数据类型还可以做其他的验证 age:{ type:Number } }支持的数据类型:Boolean、String、Number、Function、Object、Array、Symbol
(2)验证必填,只是在控制台中给出警告,不会影响程序的运行
props:{ age:{ type:Number, required:true//必填项 } }(3)默认值:如果父组件在使用子组件时没有传递子组件需要的数据时,会使用默认值来代替
props:{ 要接收的参数名:{ 方法一: default:''//默认值 方法二: default(){ return '啥也没有' } } }(4)自定义验证规则
props:{ 要接受的参数:{ type:'', validator:funcrion(形参){ 验证逻辑规则 return Boolean } } }只要父组件给子组件传递了腰验证的参数时,validator中的验证规则会自动触发
如果规则函数返回true,表示验证成功
如果规则函数返回false,表示验证失败,就会在控制台中给出验证失败的警告
通过自定义事件和$emit
数据是有父组件传递给子组件的,如果子组件想要改变数据,则要通过父组件传递一个自定义事件,然后在子组件中触发这个事件来实现数的改变
(1)父组件在使用子组件是传递一个自定义事件
<v-news v-for="(item,index) of newsArr" :key="index" //key不能当做参数传递给子组件 :title="item.title" :count="item.count" @addcount="add" :index="index"> </v-news> <script> add(m){ this.newsArr[m].count++; } </script>addcount就是一个自定义事件,对应一个父组件级别的函数
(2)子组件通过自身的methods自定义函数通过this.$emit来触发父组件传递的自定义事件
<template> <button @click="viewnews(index)">访问</button> </template> <script> export default { props: ['title','count','index'], methods: { viewnews(index){ // $emit触发父组件传递过来的自定义参数 // 第一个参数要触发的事件名称;第二个参数是要传递的参数 this.$emit('addcount',index); } } } </script>eventbus
(1)创建一个公用的容器,用来进行数据的发送和接收
在src/main.js中,vue实例化之前,给vue的原型上添加一个公共容器
Vue.prototype.$bus= new Vue();(2)$emit
在数据发送的组件中,通过公用容器中的$emit方法来触发一个函数并发送数据
sendData(){ this.容器名称.$emit('事件名','要传递的数据') }(3)$on
在其他任意组件里,通过公用容器中的$on方法来监听触发事件名称,用来接收发送端传递的数据
mounted(){ this.容器名称.$on('事件名称',(形参)=>{ ... }) }devtools是vue全家桶中的一个浏览器插件,作用是让我们开发人员能够非常清楚的了解项目的组件结构和数据及状态
安装:在谷歌浏览器中访问浏览器商店,搜索vue关键词在搜索的结果匹配中第一个就是,点击添加到chrome中
使用:
打卡某个vue项目页面后,然后打开浏览器的调试工具
为了限制样式污染,可以给style标签设置一个scoped来显示样式的作用范围仅在当前组件起作用
<style scoped> </style>(1)改变html标签的默认结构约束
<table> <tr is="h3">这是一个标题</tr> <tr is="my-child"></tr> </table>(2)动态组件
①需要结合非父子组件通信和is属性,来实现页面上,展示不同的内容
在左侧菜单栏上,
<template> <div class="menu"> <ul> <li v-for="(menu, index) of menus" :key="index" @click="sendtag(menu.tagname)" >{{ menu.title }}</li> </ul> </div> </template> <script> export default { data() { return { menus: [ { title: "系统设置", tagname: "setting" }, { title: "用户管理", tagname: "user" }, { title: "个人中心", tagname: "profile" } ] }; }, methods:{ sendtag(t){ this.$bus.$emit('changeTag',t) } } }; </script> <style lang="css"> .menu ul { text-align: center; } .menu ul li { padding: 10px; } </style>②在content组件中,引入所有需要展示的页面组件并注册,然后通过页面监听来接收数据
<template> <div class="content"> <!-- <setting></setting> <user></user> <profile></profile> --> <!-- 动态组件 --> <table :is="tagname"></table> </div> </template> <script> import setting from './Setting' import user from './User' import profile from './Profile' export default { data(){ return{ tagname:'setting' } }, components:{ setting,user,profile }, mounted(){ this.$bus.$on('changeTag',(tag)=>{ this.tagname = tag; }) } } </script>vue中一般不直接操作DOM结构,如果必须要进行DOM结构操作,可以给标签或者组件添加ref结构
(1)普通字符串
<h3 ref="myh3">XXX管理系统</h3> <script> mounted(){ this.$refs.myh3.innerHTML = '小小后台管理系统' } </script>(2)数组
如果在v-for循环的标签上,设置了ref属性,那么通过$refs获取到的DON节点的数组
<li v-for ref="myli"></li> <script> mounted(){ console.log(this.$refs.myli) } </script>(3)自定义组件
ref属性更多的应用在自定义组件上,可以实现父子组件通信的效果
注意:ref只能在挂载完成后使用
(1)直接引入jQuery.js文件
在项目根目录中引入index.html文件中
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>(2)npm安装
npm i jquery --save在vue组件中引入使用jQuery
import $ from 'jquery'把jquery挂载到原型上
import jq from 'jquery' Vue.prototype.$=jq new Vue({})把jq挂载到原型上以后,在任意的组件上都可以通this.$来使用jQuery
作用:如果需要在父组件使用子组件时,来分发一些不同的内容展示在子组件中,可以使用vue提供的插槽来实现
slot
是在子组件中,写入一个标签,设置一个插槽的位置。
这样,在父组件中使用子组件中,就可以在子组件内写入内容,并展示在子组件的插槽中
父组件:
<what> <p>父组件标题</p> <p>父组件内容</p> </what>子组件:
<div id="app"> <!-- 匿名插槽 --> <slot></slot> <h1>waht组件</h1> </div>如果子组件中有多个插槽来展示不同的内容,用匿名插槽就会出现重复的内容
为了区分不同的插槽,可以给slot来设置一个name属性来区分它们
父组件:
<what> <p slot="top">父组件标题</p> <p slot="bottom">父组件内容</p> </what>子组件
<div id="app"> <!-- 具名插槽 --> <slot name="top"></slot> <h1>waht组件</h1> <slot name="bottom"></slot> </div>在用插槽的时候,希望父组件可以控制插槽的结构和内容,子组件只做遍历循环,具体内容由父组件控制
子组件的一部分DOM结构是由外部传入
子组件:
<ul> <slot v-for="item of arr" :childitem="item" name="list"></slot> </ul>父组件:
<what> <template v-slot:list="props"> <li>{{props.childitem}}</li> </template> </what>作用:让用户能够根据不同的浏览器地址来展示不同的页面组件
SPA:single page application 单页面应用
概念:
略
安装:
(1)在进行初始化项目时,选择安装路由
@会自动找到src目录
(2)手动安装
①安装vue-router插件
npm i vue-router --save②引入并实例化,挂载到vue的配置选项上
import Router from 'vue-router' Vue.use(Router); // 实例化vue-router路由管理器 let router = new Router({ routes:[]//路由配置映射表 }) new Vue({ el: '#app', router, components: { App }, template: '<App/>' })③创建几个页面组件
④配置路由映射表规则
/src/main.js
let router = new Router({ routes:[ { path:'/login',//浏览器地址中的关键词 component:Login//与关键词匹配的页面组件 } ]//路由配置管理映射表 })⑤在App.vue中添加路由出口组件
内置组件router-view
作用:浏览器地址中的关键词和路由映射表中的关键词匹配时,会把匹配的组件模板内容展示到此处
1.在src下建立router文件夹,建立index.js
//引入核心插件 import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) //引入页面组件 import Index from '../components/Index' import Login from '../components/Login' export default new Router({ routes:[ { path:'/index', component:Index }, { path:'/login', component:Login }, ] })2.在main.js中引入
import router from './router' new Vue({ el: '#app', router, components: { App }, template: '<App/>' })当某个路由规则没有匹配到时,我们可以设置一个默认的页面,首页/404页面,重定向已经存在的某个path属性
/src/router/index.js文件中
export default new VueRouter({ routes:[ { path:'/index', component:Index }, { path:'/login', component:Login }, { path:'*', redirect:'/index' } ] })注意:如果需要浏览器地址变化,必须使用rediect属性,不能使用component属性
作用:能够让访问网站的用户,快速的访问到指定的页面
router-link和编程式导航的区别:
router-link会生成a标签,编程式当好是我们自行编写的程序代码
编程式导航中的代码会无条件执行,当要跳转的路由地址就是当前访问的地址时,会产生错误,router-link不会在跳转
(1)内置标签
router-link
必要属性:to,具体的某一个路由规则的path属性的值
选填项:
active-class=“active”,模糊匹配,可以设置激活状态的router-link的状态
exact-active-class=“select”,精确匹配,可以设置激活状态的router-link的状态
<router-link to="/index active-class="active">首页</router-link> <router-link to="/login">登录</router-link> <!-- 可变区 --> <router-view></router-view>(2)编程式导航
①push
push方法会在执行页面跳转之前,把当前访问的页面地址链接进行记录,然后再跳转
this.$router.push('path属性的值')②replace
replace方法会在执行页面跳转之前,把要跳转的链接地址覆盖当前访问的链接地址,
this.$router.replace('path属性的值')应用场景:支付成功页面后
③go
用于返回已访问过的页面地址,一般写-1,表示返回上一个界面
this.$router.go(-1)如果某个页面中还有不同的子级页面要展示,可以使用路由嵌套来实现
(1)创建页面组件
(2)定义路由规则,通过children属性来设置子级路由规则
router/index.js
routes:[ { path:'/index', component:Index, children:[ { path:'movie', component:Movie }, { path:'music', component:Music }, ] }, { path:'/login', component:Login } ]注意:子级路由规则的path属性不需要加/
(3)在一级路由规则对应的页面中放入路由出口,对应子级路由界面
父级组件中
<router-link to='index/movie'></router-link> <router-view/>注意:在子级路由的导航组件中,path属性必须包含一级路由规则/二级路由规则
不同的路由规则匹配到同一个页面上,这种路由规则,叫做动态路由
参数名前一定要有冒号,冒号后面的是一个可以变化的字符串数字的组合
在参数个数不确定时,使用动态路由的方式传递参数就不合适
我们可以使用查询参数的方式进行数据传递
(1)定义路由规则
{ path: 'article/info', component: ArticleInfo }(2)在表格页面通过点击编辑按钮,实现路由跳转并传递参数
toInfo(obj){ this.$router.push({ path:'/index/article/info', query:{id:obj.id,title:obj.title} }) }路由跳转后,链接地址会以下面的方式进行自定拼接:
query的数据类型是一个对象,对象的元素与元素之间,第一个元素会以?开头,开头+key1=value&keyN+valueN
(3)在表单页面获取路由参数
this.$route.query.id this.$route.query.title(1)设置路由规则时,添加一个name属性,给这个路由规则设置一个不能重复的名称
{ path: 'user/:uid', component: UserInfo, name:'yonghu' },(2)在页面组件跳转路由规则时,不用按照以前的手动不全一级路由、二级路由和参数的方式
toInfo(obj){ this.$router.push({ name:'yonghu', params:{uid} }) }可以给已有的路由规则,设置一个其他的名称,此时,已有的路由规则和别名都可以正常访问到指定的页面组件
{ path: '/login', component: Login, alias:'/denglu' }此时/login和/denglu,看到的是同一个页面组件
vue-router中默认由两种路由模式
一种hash(默认),浏览器地址中有#,将#后的当成路由
hash虽然会出现在url地址中,但是不会包含在http请求中,不会重新加载页面,beforeRouterUpdate生效
另一种是history,浏览器地址中没有#
利用html5的history接口中的方法实现页面跳转,history部署到服务器上,会出现404情况,需要后端配合进行配置,会重新刷新页面,beforeRouterUpdate不生效
在src/router/index.js中进行路由设置
export default new Router({ mode:'history', routes:[] })作用:让指定的路由规则满足一定的条件时,才能够被访问,可以实现请求拦截的功能。
根据路由守卫的作用范围不同,分成以下三种:
(1)全局前置守卫钩子函数
当前项目中所有的路由规则被访问之前,可以进行一定的验证操作
router.beforeEach((to,from,next)=>{ //to:目标路由地址 //from:来源路由地址 //next:函数,用来执行或者改变默认的路由规则 //next();//执行默认路由规则 let userinfo = localStorage.getItem('user'); // 如果用户已登录,就执行默认的路由规则 // 如果用户未登录,并且访问的不是登录页面,就跳转到登录页面 // 如果用户未登录,并且访问的是登录页面,就执行默认的路由规则 if(userinfo){ // 用户已登录,就执行默认的路由规则 next(); }else{ // 未登录 if(to.fullPath!='/login'){ // 访问的不是登录页面,就跳转到登录页面 next('/login') }else{ // 访问的是登录页面,就执行默认的路由规则 next(); } } })(2)全局后置守卫钩子函数
当前项目中所有的路由规则被访问之后,没有验证和拦截的功能,一般做记录
router.afterEach( (to,from) => { //to:目标路由地址 //from:来源路由地址 })(1)beforeRouterEnter
写在具体的组件中
当前组件的路由规则访问之前
beforeRouteEnter(to, from, next) { //to:目标路由地址 //from:来源路由地址 //next:函数,用来执行或者改变默认的路由规则 }(2)beforeRouteLeave
当前组件路由规则离开时,切换到其他路由规定时,此钩子函数可以获取到this
beforeRouteLeave(to,from,next){ //to:目标路由地址 //from:来源路由地址 //next:函数,用来执行或者改变默认的路由规则 //this:VueCompent }(3)beforeRouteUpdate
动态路由的参数值发生变化时,会自动执行,此钩子函数可以获取到this
beforeRouteUpdate(to,from,next){ //to:目标路由地址 //from:来源路由地址 //next:函数,用来执行或者改变默认的路由规则 },写在路由映射表配置规则中
{ path: 'user/:uid', component: UserInfo, name:'yonghu', beforeEnter(to,from,next){ console.log(to) console.log(from) console.log(next) } },为了提升项目的可维护性和可扩展性,我们把src/components目录下的组件进行目录结构的优化,把所有可以通过路由地址访问带的页面组件放到pages文件夹下,把所有页面组成部分的组件(不通过路由地址访问的组件)放到views文件夹下。
以上所有的文件夹的名称没有强制要求交这个名字,可以自行设置
调整后的目录结构为
project项目名称 -src -components -pages 存放页面组件 ... 其他目录可以根据需求自行设置 -views 存放页面组成部分的组件一旦组件目录结构调整以后,在引用该组件的地方也要跟着把引入的路径地址进行改变
是在页面组件中的style标签中添加一个lang属性(默认值为css),并把他的属性值明确设置为stylus
<style lang="stylus"></style>如果有不同的页面要使用相同的样式代码,可以在stylus中封装一个函数,把需要重复使用的样式代码放到函数中,在页面组件中引入函数即可
(1)src/common/css/fn.styl
mask(){ width 100vw height 100vh background rgba(0,0,0,0.5) }(2)在页面组件中引入
<style lang="stylus" scpoed> @import '../../common/css/fn.styl' .mask mask() </style>可以预先设置好一些初始的样式信息,包括颜色,尺寸、字体、表格、表单
/src/common/color.styl
$bgColor1 = #33ad3c $bgColor2 = #2468a2 $bgColor3 = #1b315e只要在页面组件中引入相关的.styl文件就可以使用预先设置好的变量信息
<style lang="stylus" scoped> @import '../../common/css/color.styl' .nav width 100px background $bgColor2 </style>Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
state是vuex仓库中所有的状态,类似vue组件中的data。
import Vuex from 'vuex'; Vue.use(Vuex); let store = new Vuex.Store({ state: { num:100, name:'vuex name' } }) new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })在任意组件中读取仓库中的数据
{{$store.state.num}}更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于methods
mutations: { addNum(state,txt){ state.num+=txt; } }组件中
<button @click="$store.commit('addNum',5)"></button>在页面组件中使用mutation中调用,不能执行异步操作
mutations必须是一个同步函数
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。 actions: { addNumSync(context){ setTimeout({ context.commit('addNum') },1000) } }组件中
<button @click="$store.dispatch('addNumSync')"></button>Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
示例代码
let store = new Vuex.Store({ getters:{ showNum(state){ return `最新的数量是${state.num}`; } } })在组件中使用计算属性
<p>{{$store.getters.showNum}}</p>由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
namespaced:true 启用命名空间
①设置模块
export default new Vuex.Store({ .... modules:{ shop:{ namespaced:true,// 启用命名空间 state:{ num:1 }, mutations:{ addNum(state, step) { state.num += step; } } } } })②组件中使用state和mutation
<template> <div> <h1>shop模块</h1> <p>shop:{{$store.state.shop.num}}</p> <!-- 根模块 --> <button @click="$store.commit('addNum',5)">改变根数量</button> <!-- shop模块 --> <button @click="$store.commit('shop/addNum',5)">改变shop数量</button> </div> </template>③使用助手函数
...mapGetters('命名空间名', ["getCartGoods"])当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗.
在页面组件中
<script> import {mapState} from 'vuex' export default { computed: { ...mapState(['num']) }, } </script>mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
在页面组件中
<script> import {mapState,mapGetters} from 'vuex' export default { computed: { ...mapState(['num']), ...mapGetters(['showNum']) }, } </script>使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store
在页面组件中
<button @click="addNumSync(3)">action+N</button> <script> import {mapState,mapGetters,mapActions} from 'vuex' export default { methods: { ...mapActions(['addNumSync']) } } </script>使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)
在页面组件中
<script> import {mapState,mapGetters,mapActions,mapMutations} from 'vuex' export default { methods: { ...mapActions(['addNumSync']), ...mapMutations(['addNum']) } } </script>把代码都写在main.js中使非常不明智且不合理的,因为仓库中的状态非常多时,对应的代码量也会变得非常多
(1)我们在src目录下床架一个store文件夹,zai store文件夹下再创建一个index.js
(2)然后就可以把之前写在main.js中关于vuex的代码都放到/src/store/index.js中,但是,所有的代码都放在index.js中也是不合适的,以为状态和改变状态的方法会有很多个,所以在此基础上继续进行目录结构的细分,把state、mutation、actions、getters中对应的代码分别拆分到对应的js文件中,在index.js中引入即可
优化之后的代码:
/src/main.js
import store from './store' new Vue({ el: '#app', router, store:store,//一定要把仓库挂到vue实例上 components: { App }, template: '<App/>' })/src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //引入状态 import state from './state' //引入修改状态的方法 import mutations from './mutation' //引入异步操作mutation的方法 import actions from './action' //引入计算属性 import getters from './getter' //实例化vuex仓库 export default new Vuex.Store({ state:state,//key和val相同时,state:state可以简写成state mutations, actions, getters });/src/store/state.js
export default{ num: 100 }/src/store/mutation.js
export default{ addNumByOne(state){ state.num++ }, addNumByNum(state,num){ state.num+=num } }/src/store/action.js
export default{ addNumByOneSync(context){ setTimeout(()=>{ context.commit('addNumByOne') },1000) }, addNumByNumSync(context,n){ context.commit('addNumByNum',n) } }/src/store/getter.js
export default { showNum(state){ //业务逻辑 return `最新的数量是:${state.num}`; } }状态持久化
(1)使用本地存储结合vuex
(2)使用插件实现数据持久化
安装
npm i vuex-persistedstate --save使用:
/src/store/index.js
import creatPersistedState from 'vuex-persistedstate' export default new Vuex.Store({ mutations, state, getters, actions, plugins:[creatPersistedState()] })商品列表页->商品详情页->加入购物车
(1)商品列表页–展示商品
<template> <div> <h1>商品列表</h1> <div class="list"> <div class="item" v-for="good of goodsArr" :key="good.id" @click="toInfo(good.id)"> <div class="left"> <p>商品名称:{{good.name}}</p> <p>商品价格:{{good.price}}</p> </div> <div class="right"> <img :src="good.img" alt="good.name"> </div> </div> </div> </div> </template> <script> export default { methods: { toInfo(id){ this.$router.push('/goods/'+id); } }, data () { return { } } } </script>(2)商品详情页–展示具体信息,加入购物车
<template> <div> <h1>商品详情</h1> <p>商品名称:{{info.name}}</p> <p>商品价格:{{info.price}}</p> <p>商品图片: <img :src="info.img" alt=""> </p> <button @click="addCart">加入购物车</button> </div> </template> <script> export default { mounted() { let id = this.$route.params.gid; this.info = this.goodsArr.find(item => (item.id = id)); }, methods: { //点击加入购物车按钮 addCart(){ //触发vuex中的action this.$store.dispatch('shop/addCartGoodsSync',this.info); this.$router.push('/cart') } }, data() { return { info: { name: "", img: "", price: "", id: "" }, }; } }; </script>(3)初始化vue状态,并定义好改变状态的方法
/src/store/shop/index.js定义初始状态
state:{ num:1, cartGoods:[]//定义购物车空数组 },/src/store/shop/index.js定义直接改变状态的方法
mutations:{ addCartGoods(state,obj){ state.cartGoods.push(obj)//把指定的内容追加到初始状态中 } },/src/store/shop/index.js定义触发mutations的action–可以执行异步操作
actions: { addCartGoodsSync(context,obj){ context.commit('addCartGoods',obj) } },/src/store/shop/index.js定义计算属性,方便页面去获取
getters: { getCartGoods(state){ return state.cartGoods; } }(4)购物车页–通过计算属性来获取到已经加入到购物车中的商品信息
<script> import {mapGetters} from 'vuex' export default { computed:{ ...mapGetters('shop',['getCartGoods']) } } </script>①完整移入
import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';//非常重要 Vue.use(ElementUI); new Vue({ ... render: h => h(App) });②按需引入
需要哪个组件就引入哪个组件
import Vue from 'vue'; import { Button, Select } from 'element-ui'; import App from './App.vue'; Vue.component(Button.name, Button); Vue.component(Select.name, Select);参照官网使用
2.iview
3.mint-ui
在vue项目,受对象数据类型的影响,有时直接通过下标操作数组,数组内容变化了,但是页面并没有跟着进行重新渲染。
可以通过JSON序列化来实现
vuex的mutations
addCartNum(state,id){ let idx = state.cartGoods.findIndex((item)=>item.id == id); let goodsArr = JSON.parse(JSON.stringify(state.cartGoods)); goodsArr[idx].num++; state.cartGoods = goodsArr; }可以使用vue提供的$forceUpdate方法
vuex的actions
addCartNumSync(context,id){ context.commit('addCartNum',id) }vuex的mutations
addCartNum(state,id){ let idx = state.cartGoods.findIndex((item)=>item.id == id); state.cartGoods[idx].num++; }页面组件代码
methods:{ ...mapActions(['addCartNumSync']), add(id){ this.addCartNumSync(id); //调用完成vuex中的actions操作方法对数据进行改变后 //强制重新渲染页面,触发update生命周期钩子函数 this.$forceUpdate(); } }element-ui中提供了表单中常用的组件,比如输入框、选择框、单选框、开关等
<el-form label-width="80px" style="width:600px;" > <el-form-item label="菜单名称"> <el-input v-model="info.title"></el-input> </el-form-item> <el-form-item label="上级菜单"> <el-select v-model="info.pid" placeholder="请选择"> <!-- value 设置选中项的值 label 设置选中项的名称 --> <el-option value="">请选择</el-option> <el-option value="0" label="顶级菜单">顶级菜单</el-option> <el-option value="1" label="系统设置">系统设置</el-option> </el-select> </el-form-item> <el-form-item label="菜单图标"> <el-input v-model="info.icon"></el-input> </el-form-item> <el-form-item label="菜单地址"> <el-input v-model="info.address"></el-input> </el-form-item> <el-form-item label="状态"> <el-switch v-model="info.status"></el-switch> </el-form-item> <el-form-item> <el-button type="primary">提交</el-button> </el-form-item> </el-form>其中,el-select组件中的el-option组件,如果不设置label和value属性的话,则默认把el-option中的内容当成值和label,但是实际项目中一般都不会直接把el-option中的内容进行传递,所以需要设置value属性和label属性,value属性用来控制传递的值,label属性用来控制匹配的值在option中显示的内容。
element-ui的表单组件中内置了验证功能,可以防止数据的丢失
(1)rules属性
需要给表单组件添加一个rules属性,用来告知表单具体的验证规则是什么
<el-form :rules="具体的验证规则名称">(2)验证规则
具体的验证规则需要写在data里来进行预定义
<script> export default { data(){ return{ 验证规则名称:{ 要验证的字段名:[ { required:true,message:'菜单名称不能为空',trigger:'blur' }, { min:1,max:20,message:'菜单名称长度不符合要求' } ] } } } } </script>required 设置元素必填
messeage设置元素不符合验证规则显示的文字内容
trigger 设置元素触发规则的机制
min 设置元素内容的最小长度
max设置元素内容的最大长度
(3)prop属性
给需要进行验证的表单元素设置一个prop属性,属性值要和在验证规则中设置的名称保持一致
<el-form-item label="展示的名称" prop="要验证的字段名">(4)model属性和ref属性
在进行表单验证时,需要给表单组件设置model属性,用来进行具体数据内容的验证
<el-form :model="要进行验证的数据对象" ref="表单自定义名称">(5)validate
在点击提交按钮时,需要执行表单组件内置的validate方法来实现表单内容的验证
<el-form-item> <el-button type="primary" @click="自定义方法('表单的ref属性值')">提交</el-button> </el-form-item> <script> export default { ... methods:{ 自定义方法(形参) { this.$refs[形参].validate((valid) => { if (valid) { //验证规则满足时,才执行数据添加操作 } }); } } } </script>示例代码:
<template> <div> <h1>菜单信息页</h1> <!-- el-form验证时使用的属性 rules 表单的验证规则 model 表单验证时使用的数据 --> <el-form label-width="80px" style="width:600px;" :rules="rules" :model="info" ref="menuForm" > <el-form-item label="菜单名称" prop="title"> <el-input v-model="info.title"></el-input> </el-form-item> <el-form-item label="上级菜单" prop="pid"> <el-select v-model="info.pid" placeholder="请选择"> <!-- value label 设置选中的选项名称 --> <el-option value="">请选择</el-option> <el-option value="0" label="顶级菜单">顶级菜单</el-option> <el-option value="1" label="系统设置">系统设置</el-option> </el-select> </el-form-item> <el-form-item label="菜单图标"> <el-input v-model="info.icon"></el-input> </el-form-item> <el-form-item label="菜单地址"> <el-input v-model="info.address"></el-input> </el-form-item> <el-form-item label="状态"> <el-switch v-model="info.status"></el-switch> </el-form-item> <el-form-item> <el-button type="primary" @click="submitInfo('menuForm')">提交</el-button> </el-form-item> </el-form> </div> </template> <script> export default { data(){ return{ info:{ title:'', pid:'', icon:'', address:'', status:true }, rules:{ title:[ { required:true,message:'菜单名称不能为空',trigger:'blur' }, { min:1,max:20,message:'菜单名称长度不符合要求' } ], pid:[ { required:true,message:'请选择上级菜单' } ] } } }, methods:{ submitInfo(formName) { this.$refs[formName].validate((valid) => { if (valid) { //验证规则满足时,才执行数据添加操作 } }); } } } </script> <style scoped> .el-form{ margin:20px; } </style>显示当前页面的路径,快速返回之前的任意页面。
el-breadcrumb
el-breadcrumb-item
<el-breadcrumb separator=">"> <el-breadcrumb-item :to="{path:'/home'}">首页</el-breadcrumb-item> <el-breadcrumb-item> <a href="#/menu">菜单列表</a> </el-breadcrumb-item> <el-breadcrumb-item>菜单添加</el-breadcrumb-item> </el-breadcrumb>可以直接给el-breadcrumb-item组件设置to属性来进行页面的跳转,也可以在其中添加a标签/router-link标签来进行页面的跳转,如果不需要页面跳转,则直接写文字内容即可。
el-breadcrumb-item之间的分隔符默认是斜杠,可以通过separator属性来自行设置分隔符。
default-active 当前激活菜单的 index
router 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
启用路由模式后,el-menu-item组件的index属性就是要跳转的路由地址,不需要再使用router-link标签也可以实现路由跳转。
但是dafault-active设置为固定值的话,再次刷新页面后,左侧菜单还是选中的固定值的菜单
所以需要把default-active的设置为一个变量
(1)页面加载时
/src/components/views/Nav.vue
<script> export default { data(){ return{ defaultActive:'' } }, mounted(){ //页面加载时,控住左侧菜单选中效果 //把当前路由中的meta的自定义属性赋值给默认选中变量 this.defaultActive = this.$route.meta.select; } } </script>由于在路由切换时,信息页面的路由地址并没有在左侧菜单中,可以通过路由的meta属性来自行设置选中哪个左侧菜单
/src/router/index.js
{ path:'menu', component:()=>import('../components/pages/Menu/Index'), meta:{select:'/menu'} }, { path:'menu/add', component:()=>import('../components/pages/Menu/Info'), meta:{select:'/menu'} }meta属性是路由信息中内置的一个属性,它的属性值类型为对象,在对象中自定义一个键值对用来告知左侧菜单应该选中哪个即可实现。
(2)路由地址变化时
/src/components/views/Nav.vue
<script> export default { ... watch:{ $route(newVal){ this.defaultActive = newVal.meta.select; } } }官网:https://git-scm.com/
下载:https://git-scm.com/download/win
安装:下一步即可
(1)查看配置列表
git config --list(2)设置用户名和邮箱地址
git config --global user.name "你的用户名" git config --global user.email "你的邮箱地址"(1)初始化本地仓库
进入到指定的目录后,把某一个文件夹初始化为仓库
git initmkdir 目录名 创建一个目录
touch 文件名 创建一个文件
(2)查看仓库状态
git status(3)文件添加到暂存区
git add <文件名> git add *.html 把指定的文件类型一次性全部提交 git add * 添加所有文件到暂存区(4)提交文件创建版本
git commit -m "备注信息"(5)查看日志和版本号
git log//查看日志 git reflog//查看版本号(6)版本切换
切换到上一个版本
git reset --hard HEAD^切换到上两个版本
git reset --hard HEAD^^回退到上100个版本
git reset --hard HEAD~100切换到指定的版本号上
git reset --hard 版本号(7)分支
master 主分支 可以对外发布和访问的程序
develop 开发分支 在主分支的基础上继续开发新的功能
debug 调试bug
release 预发布分支
fixed 修复分支
①查看分支
git branch②创建分支
git branch 分支③切换分支
git checkout 分支名称④合并分支
git merge 要合并的分支名称⑤删除分支
git branch -D 要删除的分支名称(1)注册账号
(2)创建仓库
(3)在本地的仓库中,添加远程仓库
git remote add origin https://github.com/lincong1498/demo.git(4)推送资源到远程仓库
git push -u origin master(5)获取远程仓库资源
①克隆远程仓库到本地
git clone https://github.com/lincong1498/demo.git一般只执行一次
②获取远程仓库资源
git pull远程仓库资源和本地的不一致,远程仓库的新
jquery-ajax:增加项目体积、fetch:比axios要麻烦
基于Promise的HTTP客户端,用于浏览器和node.js
(1)配置代理-跨域请求
Access to XMLHttpRequest at 'http://suggestion.baidu.com/su?cb=getwd&wd=123' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.这个问题是因为没有设置代理就进行了跨域请求
在vue项目中,可以配置webpack代理,实现跨域请求
项目根目录/config/index.js
proxyTable: { '/关键词':{ //http://localhost:8080/jianyi/su?cb=&wd=123 target:'http://suggestion.baidu.com',//目标域名地址 changeOrigin:true,//允许跨域 pathRewrite:{ '^/关键词':'' } } },在 proxyTable中,/关键词的作用是当vue项目发起的请求中包含了指定的关键词时,就进行代理的转发
target 要转发的目标域名地址(只包含域名)
changeOrigin:true,//允许跨域
pathRewrite路径重写规则【选填】
注意事项:
一旦配置好代理重写规则后,在具体的页面组件发起网络请求时,不要写域名,只写:/关键词/接口地址
修改了配置文件后,一定要重启项目,才会重新加载配置文件
①get请求
写法一
axios.get('请求的地址').then(result=>{ ... })写法二
axios({ url:'请求地址?参数名=参数值&参数名=参数值' }).then(res=>{ ... })写法三
axios({ url:'请求地址', params:{提交的数据}, method:'get' }).then(res=>{ ... })②post请求
写法一
axios.post('请求的地址',要提交的数据).then(result=>{ ... })写法二
axios({ url:'请求地址', method:"post", data:{要提交的数据} }).then(result=>{ ... })拿到项目后,解压到文件夹中,进入文件夹,执行命令
npm i/接口项目根目录/config/global.js
exports.dbConfig = { host: 'localhost', user: 'root', password: '数据库用户密码', port: 3306, database: 'shop_db' // 数据库名字 }官网:
官网:https://echarts.apache.org/zh/index.html
在vue项目中使用
安装
npm i echarts --save引入
import echarts from 'echarts'使用
<template> <div> <div id="main" style="height:400px;width:500px"></div> </div> </template> <script> import echarts from 'echarts' export default { data () { return { charts:'', orderData:["313","340","321","456","234","567","789"] } }, methods: { drawLine(){ var charts = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 var option = { title: { text: '近一周订单数量' }, tooltip: {}, legend: { data:['近一周订单数量'] }, xAxis: { data: ["周一","周二","周三","周四","周五","周六","周日"] }, yAxis: {}, series: [{ name: '近一周订单数量', type: 'line', data: this.orderData }] }; // 使用刚指定的配置项和数据显示图表。 charts.setOption(option); } }, mounted () { this.drawLine(); } } </script>官网:http://mint-ui.github.io/#!/zh-cn
安装
npm i mint-ui --save官网:https://www.tslang.cn/
TypeScript是js的超集,由微软开发和开源的。
通俗的理解为js的一个特殊版本,其语法规范严谨,适用于开发大型项目。
(1)安装
npm i typescript -g(2)验证是否安装成功
tsc -v(3)编译
①手动编译
创建一个index.ts,并在其中写一行普通的js代码
在命令行中进入指定的目录,执行tsc index.ts进行编译,在同级目录下会生成一个同名的js文件
②自动编译vscode
生成ts的配置文件
tsc --init此时就会在指定的目录下生成一个tsconfig.json文件
修改配置文件
“outDir”:’./'注释接口
编写index.ts
vscode->终端->运行生成任务->tsc:监视
这样在index.ts文件,编写了内容,就会自动生成对应的ts文件
在根目录下创建一个index.html并引入生成的好的js文件
ts最大的特点就是进行了严谨的数据类型验证,要求我们在声明一个变量的时候,就定义好该变量的数据类型,且赋值的内容,必须和对应的数据类型保持一致
(1)声明变量
格式:
let 变量名:数据类型 = 内容; let 变量名:数据类型;错误用法:
let str:string; str = 123;//此时会报错,数据类型不致(2)数据类型
①字符串
let str:string="初始ts";//值必须为字符串②数值
let num:number = 100;//值必须为数值③布尔值
let isbol :boolean = true;//值必须为布尔值④数组
原生
let arr = []; let arr2 = new Array;ts方式:ts定义数组,需要定义数组内元素数据类型
let arr3:string[] arr = ['12'] let arr4:Array<number>= [11,22,33];新增类型
①元组类型
元组类型是数组类型的子类型,表示一个已知元素数量和类型的数组,各元素类型可以不一样
let 变量名:[数据类型1,数据类型2,...] = [值1,值2,...] let arr5:[string,number,boolean] = ['hello',1,true]②any
any类型,表示任意类型,如果不确定我们的变量,就可以使用any类型
let obj:any = { 'name':'刘飞', age:20 } let arr6:any = []; arr6[1] = "111";③枚举类型
表示值是某些固定值中的一些
比如:性别,男、女;订单状态:已下单,已发货,已收货
格式:
enum 变量名 { 值1, 值2 }④void
一般应用于函数,表示函数无返回值
⑤never
表示那些永远不存在的数据类型
function error(msg:string):never{ throw new Error(msg) }⑥object
object表示非原始类型,也就是除了number,string,boolean,array,null,undefined之外的类型
4.函数
ts中的函数知识和es6中的函数有很多相同之处
(1)函数定义
在ts中,定义一个函数需要定义函数的返回值类型,如果没有返回值则类型为void,如果有参数也要指定参数的数据类型
function 函数名([参数名:参数的数据类型]):函数返回值的数据类型示例代码
function add(num:number):void{//函数无返回值 console.log(num); } //函数的返回值类型是数字 function add(num:number):number{ return num+=10 }②可选参数
可选参数如果没有传递,在函数中仍然用到了这个参数,则它的值为undefined
function hello(str:string,txt?:string):string{ return str+txt; } console.log(hello('你好','ts'))③剩余参数
替代传统函数中的arguments的作用
剩余参数需要是所有参数中的最后一位,不然就做不到剩余
function family(main:string,...child:string[]):string{ return '户主:'+main+',家庭成员:'+child.join(' '); } console.log(family('小王','王一','王二','王三'))ts中相关类的知识和ES6中非常相似
class Person{ name:string; constructor(username:string){ this.name = username; } speak(language:string,language2?:string):void{ console.log(`${this.name}会说${language}`) } }(2)类的继承
使用extends关键词
class Chinese extends Person{ constructor(username:string){ super(username) } speak(language1:string,language2:string){ console.log(`${this.name}会说${language1},${language2}`) } } let p1 = new Chinese('张飞'); p1.speak('汉语','应语')(3)类的修饰符
public,共有属性[默认],在当前类里面,子类、类的外部都可以访问
protected,受保护的,当前类里,子类可以访问,在类的外部无法访问
class Teacher{ protected name:string; constructor(name:string){ this.name=name; } teach():string{ return `${this.name}在讲课` } } let t1 = new Teacher('李老师'); console.log(t1.teach()); // console.log(t1.name);//在类的外部无法直接访问一个受保护的属性 class WebTeacher extends Teacher{ constructor(name:string){ super(name) } teach():string{ return `${this.name}在讲课---子类` } } let t2 = new WebTeacher('范老师'); console.log(t2.teach());private,私有,只能在当前类里面访问,不能在子类、类外部访问
class Student{ private name:string; constructor(name:string){ this.name = name; } study():string{ return `${this.name}在学习`//可以在当前类中调用私有属性 } } class MidStudent extends Student{ constructor(name:string){ super(name) } homework():void{ console.log(this.name+'在写作业');//报错,只能在Student里访问 } }(4)类的其他修饰符
①静态属性和方法
静态属性或静态方法,使用static修饰符,调用属性或方法时,不需要实例化类
通过类名.静态属性或者静态方法直接使用
// 静态属性和方法 class Util{ static desc:string = "工具类" static getTime(time:number):string{ let d = new Date(time); return d.toLocaleTimeString(); } } console.log(Util.desc) // 调用类中静态方法 console.log(Util.getTime(new Date().getTime()))②抽象类
抽象类一般作为其派生类的基类使用,它们一般被直接实例化,一般用来定义标准
abstract关键词,抽象类中必须有抽象方法,抽象方法不实现具体功能,它只做约束。非抽象方法必须自己实现功能
在子类中一旦继承了抽象类,抽象类中的抽象方法,就必须在子类中实现具体的功能。
abstract class Car { abstract drive():any; abstract stop():any; run():void{ console.log('汽车在跑') } } class BMW extends Car { drive():void{ console.log('这辆宝马在路上驾驶') } stop():void{ console.log('这辆宝马在路上停止了') } } let c1 = new BMW(); c1.drive();在上述代码中,只能校验一个变量的数据类型
如果传递的参数数量比较多或者是一个对象,那么这个函数的参数就无法实现校验
接口(Interface)是类中的一个很重要的概念,他是对行为的抽象,而具体如何实现需要由类(class)实现(implements)
(1)接口定义
interface Personal{ name:string, age:number } let pp1:Personal; pp1 = { name:'小芳', age:18 }(2)类的接口
interface Animal{ name:string; eat(str:string):void; } class Horse implements Animal{ name:string; constructor(str:string){ this.name=str; } eat():void{ console.log(this.name+'在吃粮草'); } } let hxbm = new Horse('汗血宝马')装饰器是一种特殊的类型声明,他可以被附加到类、方法、属性、参数上面,可以改变类的行为。
装饰器就是一个方法或者说是一个特殊的函数,可以被注入到类、方法、属性、参数上。
装饰器分类:类装饰器、方法装饰器、属性装饰器、参数装饰器
装饰器也是ES7里面的标志特性之一。
类装饰器实在声明类之前(紧靠类的声明处),类装饰器应用于类的构造函数,可以用来监视、修改或替换类的定义
①普通装饰器
定义装饰器
//1.普通装饰器(无法传参数) function logClass(params:any){ params.prototype.url="请求的连接地址"; params.prototype.run=function(){ console.log('run') } }使用装饰器
@logClass class HttpClient { name:string; constructor(n:string) { this.name=n; } } let http:any = new HttpClient('test'); console.log(http.url); http.run()②装饰器工厂(可以传递参数)
定义装饰器
function logClass(params:string){ return function(target:any){ console.log(target,111) console.log(params,222) target.prototype.url = "接口地址是"+params; } }使用装饰器
@logClass('www.ujiuye.com') class HttpClient { name:string; constructor(n:string) { this.name=n; } } let http:any = new HttpClient('test'); console.log(http.url);定义属性装饰器
function logProperty(params: any) { return function (target: any, attr: any) { console.log(target, 3)//类对象 console.log(attr, 4)//属性装饰器修饰的属性名 console.log(params, 5)//调用属性装饰器时传递的参数 target[attr] = params } }使用属性装饰器
class HttpClient { @logProperty('张辽')//调用属性装饰器并传递参数 name: string | undefined; } let http: any = new HttpClient();可以被运用到方法的属性描述上,可以用来监视、修改、替换方法定义
运行时,需要传递三个参数
(1)对应静态类成员来说就是类的构造函数,对于实力成员来说就是类的原型对象
(2)成员(属性)的名字
(3)成员的属性描述
定义方法装饰器
function logMethod(params:any){ return function(target:any,methodName:any,desc:any){ console.log(target,6)//类的原型 console.log(methodName,7)//方法名称 console.log(desc.value,8)//方法的描述 target.run = function(){ console.log('run') } } }使用方法装饰器
@logClass('www.ujiuye.com') class HttpClient { @logProperty('张辽') name: string | undefined; url:string | undefined; constructor() {} @logMethod('111') getData(){ console.log(this.url); } } let http: any = new HttpClient(); http.run()运行时,会被当做函数调用,可以使用参数装饰器为类的原型增加一些元素数据,需要传递三个参数
(1)对应静态类成员来说就是类的构造函数,对于实力成员来说就是类的原型对象
(2)方法名称
(3)参数在函数列表中的索引
定义参数装饰器
function logParams(params:any){ return function(target:any,methodName:any,paramsId:any){ console.log(target,9)//类的原型 console.log(methodName,10)//方法名称 console.log(paramsId,11)//参数的索引位置 } }使用参数装饰器
class HttpClient { @logProperty('张辽') name: string | undefined; url:string | undefined; constructor() {} @logMethod('111') getData(@logParams('1111') str:any){ console.log(this.url); } }先卸载已安装的脚手架,再安装新版本的脚手架(@vue/cli)
npm uninstall vue-cli -g新的包名已经改为@vue/cli
npm install @vue/cli -g检测版本
npm --version(1)
vue create 项目名称(2)步骤
创建一个页面组件,修改script代码
<template> <div>{{str}}</div> <button @click="changeStr"></button> </template> <script lang="ts">//lang="ts"表示使用ts语法 //引入类装饰器Component和Vue类 import {Component,Vue} from 'vue-property-decorator' @Component({}) export default class Home extends Vue({ str:string = '超级天王' changeStr():void{ this.str = "中原一点红" } mounted(){ console.log('组件挂载完成') } get newStr(){ return '我是'+this.str } }) </script>我们在定义data、生命周期、计算属性、事件方法、数据监听、组件引入都有一定的变化
在class组件中,没有了data和methods
(1)定义初始变量
export default class Home extends Vue({ str:string = '超级天王' })(2)定义方法
export default class Home extends Vue({ changeStr():void{ this.str = "中原一点红" } })(3)生命周期,直接在函数中以类的方式调用即可
mounted(){ console.log('组件挂载完成') }(4)计算属性
没有了computed,直接写在前面并带上一个get
get newStr(){ return '我是'+this.str }(5)组件通信
①父子组件
父组件
<template> <div> <v-child msg></v-child> </div> </template> <script lang="ts"> import {Component,Vue} from 'vue-property-decorator' import vChild from './Child.vue' @Component({ components:{ vChild } }) export default class Home extends Vue({ msg:string = "父组件上的数据" }) </script>子组件
<template> <div> <p>gitf:{{gift}}</p> </div> </template> <script lang="ts"> import {Component,Vue,Prop} from 'vue-property-decorator' @Component({}) export default class Home extends Vue({ @Prop(String) readonly gift:string |undefined @Prop({ type:string, required:true }) gift:string|undefined }) </script>②子父组件
父组件
<template> <div> <v-child :gift=msg @changStr="changeMsg"></v-child> </div> </template> <script lang="ts"> import {Component,Vue} from 'vue-property-decorator' import vChild from './Child.vue' @Component({ components:{ vChild } }) export default class Home extends Vue({ msg:string = "父组件上的数据", changeStr(str){ console.log('111'+str) } }) </script>子组件
<template> <div> <p>gitf:{{gift}}</p> <button @click="send">发生</button> </div> </template> <script lang="ts"> import {Component,Vue,Prop,Emit} from 'vue-property-decorator' @Component({}) export default class Home extends Vue({ @Prop(String) readonly gift:string |undefined @Prop({ type:string, required:true }) gift:string|undefined @Emit("changeStr") send(){ return 'aaaa'; } }) </script>③非父子组件
在项目根目录下,执行命令:
npm run build注意事项:
1.打包后的项目,代理转发失效,需要在对应的服务器上,配合后端设置代理转发
2.每个页面组件的样式,一定要写scoped,防止打包后,样式污染;根组件不写scoped
以nginx服务器为例,代理的配置
在网站域名列表处,点击设置按钮,再找到配置文件
location ^~/uploads{ proxy_pass http://服务器域名或ip地址:端口; } location /api/{ proxy_pass http://服务器域名或ip地址:端口; }重启项目生效
1.在项目根目录下,创建一个vue.config.js文件
内容如下
module.exports = { outputDir: 'dist', //build输出目录 assetsDir: 'assets', //静态资源目录(js, css, img) lintOnSave: false, //是否开启eslint devServer: { open: true, //是否自动弹出浏览器页面 https: false, //是否使用https协议 hotOnly: false, //是否开启热更新 proxy: { '/关键词': { target: '目标域名地址', //API服务器的地址 ws: true, //代理websockets changeOrigin: true // 是否跨域,默认是true } }, } }配置完成后,重启项目生效
Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
优势:
SEO优化
渲染速度快
劣势:
开发条件受限,vue的某些生命周期钩子函数无法使用
部署和构建都有要求,在node环境下运行
更多的服务器端负载
ssr本质就是将vue的语法功能延伸到服务器端,在服务器端渲染好节点内容,给客户端浏览器编译
1.创建一个文件夹,并进入到命令行中,初始化node环境
npm init2.安装ssr和vue
npm i vue vue-server-renderer --save3.将vue模板内容编译成字符串
const Vue = require('vue') const app = new Vue({ data:{ msg:'vue学习' }, template:'<div>{{msg}}</div>' }) 创建一个vue服务器端渲染对象 const renderer = require('vue-server-renderer').createRenderer() //将vue实例变成字符串内容 renderer.renderTostring(app,(err,html)=>{ if(err) throw err; console.log(html) res.end(html) })命令行编译:node app.js
4.启动一个服务器,并展示编译后的字符串
const Vue = require('vue') //引入express const express = require('express') const server = express() const app = new Vue({ data:{ msg:'vue学习' }, template:'<div>{{msg}}</div>' }) 创建一个vue服务器端渲染对象 const renderer = require('vue-server-renderer').createRenderer() server.get('/',(req,res)=>{ //将vue实例变成字符串内容 renderer.renderTostring(app,(err,html)=>{ if(err) throw err; console.log(html) res.end(html) }) }) server.listen(3000,()=>{ console,log('监听3000端口') })5.集合html文件和模板语法实现ssr
aap.js
const Vue = require('vue') //引入express const express = require('express') const server = express() const fs = require('fs') const app = new Vue({ data:{ msg:'vue学习', }, template:'<div>{{msg}}</div>' }) //创建一个vue服务器端渲染对象 const renderer = require('vue-server-renderer').createRenderer({ template:fs.readFileSync('./index.html','utf-8') }) let htmldata = { title:'服务端渲染' } server.get('/',(req,res)=>{ //将vue实例变成字符串内容 renderer.renderTostring(app,htmldata,(err,html)=>{ if(err) throw err; //console.log(html) res.end(html) }) }) server.listen(3000,()=>{ console,log('监听3000端口') })index.html
<body> //用来占位,展示编译后的字段串 <!--vue-ssr-outlet--> </body>2.初始化项目
npx create-nuxt-app 项目名称