Vue框架
一、v-model双向绑定
// 引入 vue.js 库
<script src="./vue.min.js"></script>
<div id="node">
<h1>
欢迎{{ secondName + '' + firstName}}来到Vue学习阶段!
</h1>
<!-- v-model.trim 去掉输入内容两边的空格 -->
<!-- v-model.lazy 懒监听-->
<input type="text" v-model="firstName" />
<input type="text" v-model="secondName" />
</div>
<script>
new Vue({
// 指定当前 Vue 的作用标签。
el:"#node",
// 为了避免数据全局污染,这里使用 return 产生一个闭包
data() {
return {
firstName:"",
secondName:""
}
}
})
</script>
双向绑定实现原理
<h1 id="node"> </h1>
<input type="text" oninput="inputEvt()" />
let data = { result:'' };
// Vue使用 Object.defineProperty 方法进行数据劫持。
// Vue只能兼容到 IE9,Vue3使用 Object.proxy 代发方法来实现。
Object.defineProperty(data,'result',{
// 给 data 对象的 result 属性添加 setter 和 getter 方法。
set(val) {
render(val)
},
get() {
return document.getElementById('node').innerText
}
})
function inputEvt() {
let val = event.target.value
// 给 setter 方法赋值就会调用 setter 方法。
data.result = val
}
function render(val) {
document.getElementById('node').innerText = val
}
二、常用修饰器
<div id="node">
<!-- for(let item in list) 遍历数据,for...in机制 -->
<tr v-for="item in list">
<td>{{item.name}}</td>
<td>{{item.time}}</td>
<!-- 条件渲染指令,不符合条件的节点将不会渲染到dom树上 -->
<td v-if="item.temp <= 40">{{item.temp}}</td>
<td v-else-if="item.temp <= 70">{{item.temp}}</td>
<td v-else>{{item.temp}}</td>
<!-- 显示渲染指令,控制display属性 -->
<td v-show="item.temp <= 40">{{item.temp}}</td>
<td v-show="item.temp <= 70 && item.temp > 40">{{item.temp}}</td>
<td v-if="item.temp > 70">{{item.temp}}</td>
</tr>
</div>
new Vue({
data() {
// 因为要给Vue实例进行数据双向绑定,因此需要返回。
return {
list:Array(10).fill('').map((it,i) => {
return {
name:'admin' + i,
time:'12:3' + i,
temp:Math.ceil(Math.random()*100)
}
})
}
}
// 挂载方法,在vue中直接调用vue实例的方法需要使用一个"$"来说明是vue方法。
}).$mount('#node')
v-if 和 v-show 的区别
相同点:都可以动态控制着dom元素的显示隐藏。
不同点:v-if 的显示隐藏是将DOM元素整个添加或删除,v-show 的显示隐藏是为DOM元素添加css的样式display,设置none或者是block,DOM元素是还存在的。
三、vue事件绑定
<div id="node">
<!-- 使用v-on:来指定当前绑定的是什么事件 -->
<button v-on:click="vueEvt()">按钮</button>
<!-- v-on:click 缩写 @click -->
<button @click="vueEvt()">按钮</button>
</div>
new Vue({
// vue的methods属性用作定义方法的容器
methods:{
vueEvt() {
console.log('这是一个vue事件')
}
}
}).$mount('#node')
四、vue属性绑定
<div id="node">
<!-- v-bind:用来绑定数据 -->
<span v-bind:class="temp" v-bind:style="{color:num>70?'red':'blue'}"></span>
<!-- v-bind:简写 : -->
<span :class="temp" :style="{color:num>70?'red':'blue'}"></span>
</div>
五、虚拟节点
// 固定的一个项目入口文件
// 需要引入的 Vue 对象
import Vue from 'vue'
// 实例化vue
new Vue({
// render(createElement:CreateElement):VNode
// vue中有一个render函数,这个函数接收一个CreateElement,要求方法返回一个VNode -> virtual node 虚拟节点
render(createElement) {
// createElement(VNodeTag:String , VNodeTata:Object , VNodeChildren:Array):VNode
// createElement方法接收三个参数,并且会返回一个VNode
let em = crateElement(
'em',
{},
['我是h1的子节点']
)
let VNode = createElement(
'h1',
{
"class":'my-cls',
style:{
color:'#f00',
font-size:'20px'
}
},
[
'我是第一个子节点',
em
]
)
return VNode
}
// 挂载后会自动注入public包下的index.html文件id="app"节点
}).$mount('#app')
虚拟DOM的优点
真实DOM更新节点,需要查找节点,更新内容,再渲染。
虚拟DOM是批量更新数据,然后更新虚拟DOM对象,最后挂载到DOM树上,只需要一次查询(对根节点进行查询),全部一次挂载。
六、Vue脚手架
针对 Vue 框架进行工程化后的一个封装,提供开发、打包、测试功能;通过前端构建框架(webpack)来构建一个 Vue 的开发环境和打包工具。
全局安装
$ npm i -g @vue/cli
初始化项目
// 创建新项目
$ vue create 项目名
// 选择手动配置选项
$ * Manually select features
// 选择 Vue 版本
$ * Choose Vue version 2.x
// 选择 ESLint 检查标准版本
$ * ESLint + Standard config
// eslint 检查时机
$ * Lint on sava -- 保存时进行检查,可以预先指定代码错误,由webpack中eslint-loader插件支持
// 项目的全局配置
$ * In dedicated config files
vue.config.js文件
module.exports = {
// 关闭eslint-loader代码检查报错
lintOnSave: false,
// 配置多页应用
pages: {
// 每一个页面需要有一个名字,还应该有很多属性
index: {
// 指定当前应用的模板
template: 'public/index.html',
// 指定当前应用的入口文件
entry: 'src/main.js'
},
// 配置home页面
home:{
template:'public/home.html',
entry:'src/home.js'
}
},
// 开发服务设置
devServer:{
// 设置一个启动端口
port:16888,
// 用浏览器打开并运行项目
open:true,
// 代理属性
proxy:{
// 定义一个代理字段,请求地址中,包含了"/apis"这个字段,就会执行这个代理配置
'/apis': {
// 目标服务器
target:"http://www.shuiyue.info:14600",
// 去掉请求地址中的"/apis",替换为""。
pathRewrite:{'/apis':''}
}
}
}
}
七、组件
// src包下创建views组件包,并创建indx.vue文件
// .vue文件会被vue-loader加载器进行编译和加载
// 模板标签,会被vue-loader编译器编译为一个render方法
<template>
<!-- 顶层元素只能由一个,不能放多个兄弟节点 -->
<div class="box">
<h1>我是一个组件标题内容</h1>
<h2>我是一个组件的副标题</h2>
</div>
</template>
<script>
// 不需要使用new关键字,只需导出一个对象即可,会被vue-loader自动编译
export default {
// 如果存在template,此方法会被覆盖 -- 方法重载
render(createElement) {
return createElement(
'div',
{},
['我是一个节点']
)
}
}
</script>
// 使用style来编写样式
// scoped属性 -- 声明当前样式仅限于此页面使用
<style scoped>
.box{
margin:auto;
border:solid 1px;
}
</style>
引入组件
<template>
<div>
<!-- 把注册的组件当作一个标签使用 -->
<child/>
</div>
</template>
<script>
// 组件引入,可以不写.vue后缀
import ChildNode from './child'
export default {
// 使用vue中的components方法进行组件注册
components:{
child:ChildNode
}
}
</script>
全局组件
// main.js 项目入口文件
import Vue from 'vue'
// 引入index组件,ES6木块话的寻址功能,引入一个包,自动查找包中的index.xx文件
// 这个App是一个组件,也是一个Vue实例对象
import App from './views'
exprot dafault {
install(Vue) {
// 注册全局组件
Vue.component('组件名',path)
// 使用VueConstructor的filter方法进行过滤器全局注册
Vue.filter('自定义过滤器名字',function(value,type) {
console.log(arguments)
})
// 使用VueConstructor的directive方法进行指令全局注册
Vue.directive('自定义指令名字',function(el,bindData,newVNode,oldVNode) {
console.log(arguments)
})
}
}
vue.use 和 vue.component
// 定义插件
install(Vue){ }
// 注册插件
Vue.use(插件名)
// 注册组件
Vue.component('组件名',path)
八、组件的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CTqU5RPR-1631554755419)(E:\井振昊\笔记\三阶段\生命周期.png)]
九、组件通信
父组件传值给子组件
<!-- 在父组件种的子组件标签上使用绑定语法给子组件绑定一个属性 -->
<child-node :rate="myRate"/>
// 子组件
<script>
export default {
// 父组件通过属性传递下来的数据,使用props属性来接收
props:{
rate:{
// 指定必传参数
required:true,
// 指定接收的数据类型
type:Number,
// 指定默认值
default:0
}
}
}
</script>
子组件给父组件传值
<template>
<button @click="editEvent()">传值</button>
</template>
<script>
export default{
methods:{
editEvent() {
// emit是vue自带的方法,通过this.$emit调用
// $emit(event:string,args:...any[])
// event -> 自定义一个回调事件名字
// args:...any[] -> 触发事件时候传递的参数
this.$emit('dataEvt','传给父组件的值')
}
}
}
</script>
<template>
<my-child @dataEvt="myEvent" />
</template>
<script>
exprot default{
methods:{
myEvent() {
console.log('接收到的参数',arguments)
}
}
}
</script>
十、动态组件
<template>
<!-- component是vue中定制的一个全局组件,用于动态渲染组件 -->
<!-- 需要有一个 is 绑定属性,属性值为组件的名字 -->
<component :is="注册组件名" />
</template>
十一、全局组件
// main.js文件中注册全局组件
// 先引入组件
import ModalNode from './compinents/Modal.vue'
// 单个注册
Vue.component('ModalNode',ModalNode)
// main.js文件中自定义vue插件
// 如果自定义插件是一个函数
function plugin(MyVue) {
MyVue.component('ModalNode',ModalNode)
}
// 如果自定义插件是一个对象
const plugin = {
install:function(MyVue) {
MyVue.component('ModalNode',ModalNode)
}
}
// compinents组件包中创建index.js文件
// 引入组件
import ModalNode from './Modal.vue'
// 自定义插件
const plugin = {
install(Vue) {
Vue.component('ModalNode',ModalNode)
// 过滤器注册
Vue.filter('format',function(value) {
return newValue
})
// 自定义指令注册
Vue.directive('format',function(el,baindData) {
console.log(arguments)
})
}
}
// 导出插件
export default plugin
// main.js中直接注册
import plugin from './components'
Vue.use(plugin)
十二、事件广播
// main.js文件
new Vue({
// 定义一个全局变量
data() {
return {
// 定义一个new Vue()实例对象
eventBus:new Vue()
}
},
render:h => h(App)
}).$mount('#app')
// 触发自定义事件
this.$root.eventBus.$emit('全局回调事件')
// 监听
this.$root.eventBus.$on('监听事件名',function() {
console.log(arguments)
})
十三、继承和混合
<script>
引入数据对象
import Paging from './common/Paging.vue'
import Header from './common/Header.js'
export default {
// vue实例继承,只能实现单继承,包括原型链继承
extends:Paging,
// 仅将引入实例对象的属性值和当前的属性值进行混合,不能继承原型链
mixins:[Paging,Header]
}
</script>
十四、组件插槽
// 子组件
<template>
<!-- 具名插槽 -->
<slot name="title"/>
<!-- 默认插槽 -->
<slot/>
</template>
// 父组件
<template>
<child-node>
<!-- 具名插槽 -->
<sapn slot="title">title插槽</sapn>
<!-- v-slot传参必须用在template上,或者组件上 -->
<template v-slot:header="scope">
<p>我是scope插槽</p>
</template>
<!-- 默认插槽 -->
<!-- slot-scope用来传参,scope接收到一个对象 -->
<h1 slot="title" slot-scope="scope">我是插槽</h1>
</child-node>
</template>
十五、监听和计算属性
<script>
export default {
// 监听属性,用于监听一个数据对象的变化
watch: {
// 监听属性的入参为:改变后的值,改变前的值
dept(newValue,oldValue) {
console.log(newValue,oldValue)
},
dept:{
// 立即监听
immediate:true,
// 深监听,监听对象内部属性的变化
deep:true
// 处理函数
handler:function(newValue,oldValue) {
console.log(newValue,oldValue)
}
}
},
// 计算属性
computed:{
// methods,computed方法名不能重名,重名字后,只会调用methods的方法
myDutie:function() {
return Date.now()
}
}
}
</script>
十六、路由
// 安装路由插件
$ npm i -S vue-router
// main.js入口文件
// 引入路由插件
import Router from 'vue-router'
// 注册路由插件
Vue.use(Router)
// 引入页面组件
import LoginPage from './views/login'
// 对Router进行实例化
const router = new Router({
// H5的history模式
mode:"history",
// routes 用于路由配置,接收一个路由配置对象的数组
// path 属性用于自定义访问页面组件的路由
// component 属性用于指定当前这个路由配置对象对应的页面组件是谁
// 将path重新定向到一个路由
routes:[{path:'/',redirect:'/login'},
{path:'/login',component:LoginPage},
{
path:'/order',
component:'./order',
// children属性实现子路由,path属性前面不要带/
children:[
{path:'edit',component:'./order/edit'},
{path:'list',component:'./order/list'}
]
}
]
})
new Vue({
// 把router实例对象注入到当前Vue实例对象中
// Vue实例对象给出了一个router属性用于router实例注册
// router:router,
router,
render:ce => ce(App)
}).$mount('#app')
<template>
<!-- 路由缓存 -->
<keep-alive>
<!-- 路由中配置的组件应该放置在这个地方 -->
<router-view></router-view>
</keep-alive>
</template>
动态路由
// 定义一个动态路由
{path:'/user/:id',component:user}
// 在组件内部获取路径中定义的动态部分id
this.$route.params
路由query传参
// 路由
let router = new VueRouter({
routes: [
{ path: '/login', component: login },
{ name:'register',path: '/register', component: register }
]
})
// 导航
<router-link to="/login?id=10&name=jj">登录</router-link>
<router-link :to="{path:'/register',query:{id:5,name:'lili'}}">注册</router-link>
<router-link :to="{name:'register',query:{id:5,name:'lili'}}">注册</router-link>
// 等同于
this.$router.push('/login?id=10&name=jj')
this.$router.push({path:'/register',query:{id:5,name:'lili'}})
this.$router.push({name:'register',query:{id:5,name:'lili'}})
jquery可以通过name或path来引入路由
// 获取参数
this.$router.query
路由params传参
// 路由
let router = new VueRouter({
routes: [
{ path: '/login/:id/:name', component: login },
{ name:'register',path: '/register/:id/:name', component: register }
]
})
// 导航
<router-link to="/login/10/jj">登录</router-link>
<router-link :to="{name:'register',params:{id:5,name:'lili'}">注册</router-link>
// 等同于
this.$router.push('/login/10/jj')
this.$router.push({name:'register',params:{id:5,name:'lili'}})
// 获取参数
this.$router.params
路由拦截
/**
* NavigationGuard导航卫士
* @param to Route:即将要进入的目标,路由对象
* @param form Route:当前导航正要离开的路由
* @param next Function:路由跳转
*/
// 配置路由时,添加requireAuth属性用来判断该路由的访问是否需要登录
{path:'/manage',name:'manage',component:'manage',meta:{requireAuth:true}}
router.beforeEach((to,from,next) => {
if(to.meta.requireAuth) { // 判断该路由是否需要登录权限
if(to.path === '/login') { // 判断是否登录保存token
next()
} else {
let token = sessionStorange.getItem('token')
if(!token) {
next('/login')
} else {
next()
}
}
} else {
next()
}
})
十七、状态管理器
状态管理器是什么
- 状态管理器实际上就是全局对象,用于存储状态数据。
- 全局数据不能对全局造成污染 – 闭包,所以这个库必须提供两个方法:获取方法(getter)、设置方法(setter)
插件引入
// 安装插件
$ npm i -S vuex
// 引入vuex插件
import Vuex from 'vuex'
// 把插件注册到Vue构造函数上
Vue.use(Vuex)
// 状态管理器实例化
const store = new Vuex.Store({
// 使用modules属性来实现,使用模块化进行状态数据管理
modules:{
// 定义命名空间(作用域)名字
common:{
// 开启命名空间
namespaced: true,
// 在state属性上定义需要存储的状态数据
state:{
token:'init value',
userInfo:{id:'admin',name:'超级管理员'}
},
// 获取数据的getter方法
getters:{
// 定义一个获取token的getter方法
// 这个getter方法入参一个state对象进来
getToken(state) {
return state.token
}
},
// 改变一个状态数据的值的时候,需要使用mutations扭转属性,它相当于一个setter方法
mutations:{
// 扭转token,除了入参state对象,还会有用户传递进来的一个数据对象
mutationToken(state,token) {
state.token = token
}
},
// 定义一个执行异步动作的方法
actions:{
tokenAction(myStore,token) {
setTimeout(() => {
myStore.commit('mutationToken','AYSYNC' + token)
},1000)
}
}
}
}
})
new Vue({
// store实例注入
store,
render:ce => ce(App)
}).$mount('#app')
组件中使用vuex
// 获取命名空间中的状态数据
// this.$store.getters['命名空间/getter方法名']
this.$store.getters['common/getToken']
// 调用mutations中的扭转方法
// this.$store.commit('命名空间/扭转方法名',传递的数据)
this.$store.commit('common/mutationToken',token)
// 调用异步来更i性能token数据
// this.$store.dispatch('异步方法名',传递的数据)
this.$store.dispatch('tokenAction',token)
vue-persistedstate第三方持久化状态管理库插件
// 安装第三方持久化状态管理库插件
$ npm i -S vuex-persistedstate@3
// 引入插件
import persistedState from 'vue-persistedstate'
// 注入插件
const store = new Vuex.Store({
plugins:[
persistedState({
// 指定数据缓存到哪个webStorage中
storage:window.sessionStorage
})
],
modules:{
common
}
})
// 导出
export default store
十八、axios第三方库
// 安装第三方库
$ npm i -S axios
// 引入axios第三方库
import axios from 'axios'
// 使用axios的创建方法,可以达到配置通用数据的功能
const NewAxios = axios.create({
baseURL:'/apis',
// 设置请求接口的请求时间
timeout:5000
})
// 封装ajax
function Ajax(req) {
// 设置url必传,否则报错
if(!req.url) throw Error('请求地址必传')
return new Promise(resolve => {
// Promise嵌套
NewAxios.request({
url:req.url,
method:req.method || 'GET',
data:req.data || {},
params:req.params || {}
}).then(({data}) => {
reslove(data)
}).catch(e => {
resolve(e)
})
})
}
export default Ajax
// axios的拦截器(请求前)
// config是请求对象,里面有请求地址、请求数据、请求方式等属性
NewAxios.interceptors.request.use(config => {
// 从vuex获取token
let token = store.getters['common/token'],
// 定义不需要拦截的路由
otherPath = [
'/user/validate/code',
'/user/app/login'
]
// 判断请求的路由是否需要拦截
if (otherPath.includes(config.url)) {
return config
} else {
if(!!token) {
// 在headers上添加一个token数据,用于后端请求权限校验
config.headers.token = token
// 如果通过拦截,返回一个请求对象
return config
} else {
// 请求被拦截,返回一个Promise,axios不会发起异步请求
return Promise.reject({code:440,message:'axios拦截'})
}
}
})
十九、实现组件集成
App端很多的组件库:echarts、vant、ant-design-mobile(vue)、vux、mint、ionic,根据官网文档使用。
二十、自定义指令
<template>
<h1 v-format="data">Hello Word!</h1>
</template>
<script>
export default {
directives:{
// 函数形式
format(el,bindData,newVNode, oldVNode) {
console.log(arguments)
// el -> 页面元素
// bindData -> 绑定的数据对象
// newVNode -> 当前虚拟DOM
// oldVNode -> 更新前的虚拟DOM
},
// 对象形式,有完整的生命周期
format:{
// 指令绑定时候触发
bind() {
console.log(arguments)
},
// 虚拟节点插入到页面元素上时执行
inserted(el,bindData) {
console.log(arguments)
},
// 更新
update() {
console.log('component will update')
},
compinentUpdated() {
console.log('component updated')
},
unbind() {
console.log('unbind')
}
}
}
}
</script>
二十一、过滤器
<template>
<!-- 过滤器用法{{绑定数据 | 过滤器方法名}} -->
<h1>{{data | format}}</h1>
</template>
<script>
export default {
filters:{
// 过滤器中的参数是绑定的数据
format(data) {
console.log(arguments)
// 过滤器需要返回一个值,返回值会在页面上进行渲染
return newData
}
}
}
</script>
二十二、父子组件传值(不建议使用)
<script>
export default {
created() {
// 相当于父组件中的this
console.log( this.$parent )
// 获取所有的 VueComponent 子组件,是一个数组
console.log( this.$children )
}
}
</script>
二十三、$nextTick
<script>
export default {
methods:{
changeValue() {
this.$nextTick( () => {
// 页面数据渲染后执行的业务逻辑代码
} )
}
}
}
</script>
<script>
export default {
methods:{
async changeValue() {
await this.$nextTick()
// 页面数据渲染后再执行这里的业务逻辑代码
}
}
}
</script>
mat}}
## 二十二、父子组件传值(不建议使用)
```vue
<script>
export default {
created() {
// 相当于父组件中的this
console.log( this.$parent )
// 获取所有的 VueComponent 子组件,是一个数组
console.log( this.$children )
}
}
</script>
二十三、$nextTick
<script>
export default {
methods:{
changeValue() {
this.$nextTick( () => {
// 页面数据渲染后执行的业务逻辑代码
} )
}
}
}
</script>
<script>
export default {
methods:{
async changeValue() {
await this.$nextTick()
// 页面数据渲染后再执行这里的业务逻辑代码
}
}
}
</script>