前引:写这篇文章的目的是为了让自己回头看的时候能快速捡起当初的一些知识,同时也分享出来共大家学习讨论
一、vue的介绍
1.引导
框架和库有什么区别?
框架改变了编码思想,而库不改变只是多个函数封装的集合
vue的编码思想
面向数据,自身只考虑数据vue会帮你进行dom操作
2.前端MVC
封装好数据和视图,根据业务逻辑调用这些封装来完成事件
3.vue的MVVM
- Model是一个个{}对象存放着数据
- 简单来说VM层将数据库里的数据和绑定在视图上的数据进行同步
二、数据绑定
1.数据绑定
接收的数据类型
number/string/boolean/array/json/undefined/null/NaN
- undefined/null/NaN 不渲染
- 其他默认转字符
- 视图中的{{name}}找不到匹配项,则{{name}}整体转为空字符串''
绑定方式
- 插值 {{name}}
- 指令 v-text="绑定内容" / v-html="绑定html类型内容"
- 属性 :src="url"(让src属性的属性值可以替换成绑定的url) / :[proname]="数据"(将属性名替换成绑定的proname的值)
2.列表渲染
语法
<li v-for="值 in 数据">{{值}}</li>
<li v-for="值 of 数据">{{值}}</li>
<li v-for="(值,索引) in 数组">{{值}}/{{索引}}</li>
<li v-for="(对象,索引) in|of 数组">{{对象.key}}/{{索引}}</li>
<li v-for="(值,键) in 对象">
<li v-for="(数,索引) in 数字">
<li v-for="(单字符,索引) in 字符">
3.条件渲染
语法
v-show
v-if
对比
v-show通过给dom添加display:none来让元素脱离文档流但dom依赖存在,而v-if是增删dom性能消耗大
思考
代码扁平化是什么?
减少模块层次,尽量平铺
为啥指令的形式会被xss攻击?
可以注入html内容引入不同功能
三、事件绑定
1.语法
v-on
v-on:click='函数名'
@@click='函数名'
2.vm中的
添加个methods:{},里面放函数方法,推荐简写为=>函数名(){}
3.特殊
- 不传参默认会传递事件对象
- 使用箭头函数this会丢失
四、双向绑定
1.场景
可以用用户产生数据的地方
2.语法
v-model
v-model='数据名'
3.扩展
原生js来实现双向绑定
vue2:Object.defineProperty
vue3:proxy和反射
五、非响应式情况
1.情况
- 数组使用非变异方法
- 修改数组的长度时
- 修改数据的根索引时
- 给对象添加不存在的属性时
2.解决
尽量避免这些情况
六、key的问题
1.产生原因
vue的虚拟dom算法是尽可能减少dom移动,所以就会用修补/重用相同类型的元素的情况
vm中对dom的操作是先对虚拟dom操作到最后才渲染到视图上
2.语法
:key=""
七、模板表示式
1.语法
{{num + 9}}
{{`${str} me`}}
{{str.split(' ')}}
{{bl ? '处' : '非处'}}
2.注意
不能使用编程语句 -> {{var a = 10}}
思考
如何理解事件对象?
操作视图时记录了一系列信息的对象
数组使用非变异方式返回的是新数组,只要接收新数组来覆盖原数组就能解决
八、计算属性
1.场景
需要对data中的数据重新计算的时候
2.语法
{{函数名}}
在vm中,computed:{函数名(){return...}}
3.注意
- 其是缓存形式,访问data中的数据不变就调用缓存,变化后会再次执行
- 当成属性用
4.computed VS method
九、属性检测
1.场景
需要在数据变化时执行异步或开销较大的操作时使用(因为计算属性时同步的)
2.语法
在vm中
watch:{
1.数据名:'method函数名' //数据名==data的key
2.数据名:函数体(newValue,oldValue){}
3.数据名:{
handler(newValue,oldValue){},
deep: true //深度检测 针对符合类型
immediate: true //首次运行
}
3.注意
- 一般的浅操作可以使用,数据名:函数体(newValue,oldValue){}
- 替换是浅操作,修改是深操作
- 旧值将与新值相同,因为它们的引用指向同一个对象/数组,vue不会保留变更之前值的副本
4.计算属性 VS 函数 VS 属性检测
十、样式操作
1.语法
:class="数据|属性|变量|表达式"
:style="数据|属性|变量|表达式"
2.属性值类型支持
字符/对象/数组
<div :class="{active:true,t1:false}"></div>
<div :style="[{css属性名:值},{'xx-xx-xx':值}]"></div>
十一、指令
1.系统指令
概述
扩展了html语法功能,区别了普通html属性
v-pre
- 使用vue语法可以不被解析,{{数据}}
- 原生的
<pre>标签还是会解析vue语法
v-once
只渲染一次数据,之后数据改变不重新渲染
v-cloak
防闪烁,防止vue加载过慢暴露出{{数据}}的格式,v-cloak在vue识别到后会将其删除,而刚开始属性选择器选中v-cloak属性将其标签隐藏
css:[v-cloak]{display:none}
html:<div v-cloak></div>
2.自定义指令
概述
指令是个函数|对象,用来操作dom,里面的this返回window
全局
html: v-指令名 / v-指令名="'str'"
js: Vue.directive("指令名",function(el,binding){})
局部
html:v-指令名 / v-指令名="'str'"
vm:directive:{ 指令名(el,binding){} }
3.要点
- 可以设置在全局也可以设置局部
- el 返回的是绑定的元素
- binding记录了一些绑定元素的信息
4.拖拽指令
// 拖拽
Vue.directive("drag",function(el,binding){
el.onmousedown = function(ev){
// clientX 点击位置距离屏幕左端的距离,offsetLeft 元素距离屏幕左端的距离
// ev 是事件对象
// disX,disY 是鼠标点击点距离元素左边距和顶点的距离
let disX = ev.clientX - el.offsetLeft;
let disY = ev.clientY - el.offsetTop;
document.onmousemove = function(ev){
el.style.left = ev.clientX - disX + 'px';
el.style.top = ev.clientY - disY + 'px';
}
document.onmouseup = function(){
document.onmousemove = document.onmouseup = null;
}
return false;
}
})
思考
需要特定执行时期的函数,叫钩子函数?(对)
十二、axios
1.基本形态
axios.请求姿势(url,{配置}).then(成功回调(res)).catch(失败回调(res))
2.访问本地json
成功返回的数据
3.其他
将axios提到vue全局中使用
//main.js
import axios from 'axios';
Vue.prototype.axios = axios; (挂载到vue原型链上)
十三、跨域
1.概述
浏览器有同源策略,就是两个网站 协议/域名/端口号 都一致才能互相调用请求接收数据
2.允许跨域
3.解决方法
后端解决
部分接口允许
全部接口允许
前端解决
jsonp
浏览器装插件 正向代理
开发环境做反向代理
理解:本地服务器发送的请求被代理服务器截获包装后发向真实的服务器(将不同的域名转换成相同的)
配置步骤
1.在vue.config.js中配置代理服务器
2.在请求接口处取别名
思考
1. npm
- -S:部署到服务器上也需要用到的包
- -D:只在本地开发的时候要用到的包
2.如何理解node接口和普通http接口?
由不同的软件开发的接口
十四、构建环境
1.全局安装脚手架
npm install -g @vue/cli
2.初始化开发环境
vue create 目录
3.选择配置
4.运行
npm run serve
十五、组件
1.理解
需要复用的部分才拆出来变成组件
2.调用组件
<组件名></组件名>
<组件名 />
3.定义组件
<template> //html
</template>
<script> //js
export default {
// 选项
}
</script>
<style> //css
</style>
4.注册组件
全局注册
在main.js中注册
import 组件变量名 from "...";
Vue.component("组件名",组件变量名);
局部注册
在APP.vue中注册
import 组件变量名 from './components/组件文件名.vue';
components:{ //选项
组件名:组件变量名 //√
}
5.组件数据
限制
data选项必须是一个函数,且要有返回object,作用域独立
语法
data(){ return{} }
6.组件避讳
- 组件名不可和html同名
- 组件没有el选项,只有根实例存在el
- 组件的模板一定要有根元素(就是template下只能有一个标签)
- 组件的data是个函数,需要返回对象
7.书写风格
- 组件变量名: 大驼峰(XxxXxx)
- 组件名: 烤肉串(xxx-xxx) 或者 大驼峰(XxxXxx)
- 组件文件名: 烤肉串(xxx-xxx) 或者 大驼峰(XxxXxx)
8.css规则
原理
是把每个组件的样式抽出来插入到网页中,顺序不定
解决办法
- 自己重命名(用BEM风格)
- 自动重命名(css模块化)
<div :class="$style.box"> <style module>- 独立样式作用域(推荐)
<style scoped>// 会自动给标签添加上个属性,变成属性选择器
其他
1.es6模块化的输入输出
输入
import a from './mod/a.js
输出
export default any 默认输出只导出一次
2.key === string
用不用引号括起来都行
3. 关掉eslint方法
思考
如何理解组件就是一个对象,需要暴露,输出 ?为啥对象就要暴露?这里的export是导出给main.js中vue实例,再返回回来,这样就只需要创建一次实例?
因为模块化开发每个组件都是独立都作用域,每个都要将自己做好的虚拟dom暴露出来给到APP.vue整个成完整的虚拟dom,再给到vue的根实例进行渲染出真实的dom
APP.vue下 <template>下只能有一个标签?
对的,规定
十六、生命周期
十七、组件通讯基础
1.父子
概述
父组件通过属性绑定将数据传给子,子用props接收
语法
父
<子:自定义属性="父数据"></..>
子
props:['自定义属性']
<div>
{{自定义属性}}
</div>
注意
props是只读的,不推荐改
props命名:
props: ['postTitle']
<xx :post-title="..."
2.子父
概述
在父组件中给子组件标签添加自定义事件接收,子组件用特定方法传递
语法
父
<template>
..
<子 @自定义事件="父方法"></..>
..
</template>
<script>
export default {
methods:{
父方法(接受数据){处理}
}
}
</script>
子
<script>
this.$emit('自定义事件',子.数据名)
</script>
3.集中管理
概述
把数据放到放到根实例的data中
语法
new Vue({
data:{ a:1 }
}
使用
$root.数据名
思考
为啥组件标签上用不了v-mode?感觉很多vue中可以给html添加的属性,无法作用于自定义的组件标签上
单向数据流只能通过父子、子父、集中式管理来传递数据,子组件可以绑定@input,再通过emit发送子组件获取的内容给父组件v-model绑定的数据
十八、插槽
1.作用
将dom或组件插入到另一个组件中
2.调用组件时传递
3.组件内部接收
默认内容当外部有插入东西时被覆盖
十九、ref
1.理解
相当于标签中的id,定位dom或组件,来进行一些操作
2.使用
this.$refs.
二十、第三方组件
1.单组件
在npm上找开发文档跟着操作
2.组件库
pc端、后台管理
- element-ui 饿了么 √
- iview 个人
- ant design 蚂蚁金服 √
elementUI 安装
npm i element-ui-S
整体引入
//main.js 下
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
引入自定义主题
把导入的css改成自己本地下载下来的
移动端、客户端
- vant2 有赞 电商 √
- mint-ui 饿了么
- vue-material
- muse-ui
- VUX
- cube-ui
- vonic
- Vue-Carbon
- YDUI
vant2
安装
npm i vant@latest-v2 -S
引入
//main.js
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
引入自定义主题
//vue.config.js
css: {
loaderOptions: {
less: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
lessOptions: {
modifyVars: {
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)@blue是主题色
hack: `true; @import "./src/assets/css/vant-theme.less";`,//vant官网copy下来修改后的less变量文件
},
},
},
},
},
//main.js
import Vant from "vant";
import "vant/lib/index.less";
注意
vant是采用375的设计稿,px转rem时需要设置一下
通用
- bootstrap5/4
- ameizi
其他
1.组件的思想
尽量把能控制的东西交给外面
2.修改组件样式依照下列顺序
1. 修改主题 *
2. 使用props 权重**
3. 添加 class/style ,影响的是组件template的根元素
4. 审查元素,查询相关样式名,修改编译后的样式 + scoped ***
5. 样式穿透
css解决: .a >>> .b { /* ... */ } 深度选择器
Sass解决: .a{ /deep/ .b{} }
3.自定义属性传值简写
- 字符串:type="data"
- 布尔值:border
- 一般变量::laber="laberData"
二十一、路由
1.概述
用来SPA页面跳转(单页面跳转)
2.单页VS多页
- 单页面模式:相对比较有优势,无论在用户体验还是页面切换的数据传递、页面切换动画,都可以有比较大的操作空间
- 多页面模式:比较适用于页面跳转较少,数据传递较少的项目中开发,否则使用cookie,localstorage进行数据传递,是一件很可怕而又不稳定的无奈选择
3.基本使用
安装
npm i vue-router@3 -S(vue2)
引入注册
// src/main.js
import router from './plugins/router.js'
new Vue({
router
})
配置路由
// 引入vue
import Vue from 'vue';
// 引入路由包
import VueRouter from 'vue-router';
// 安装插件到vue上
Vue.use(VueRouter);
// 引入组件
import Home from '../pages/Home';
import User from '../pages/User'
// 路由配置
let routes = [
{ path:'/home',component:Home },
{ path:'/user',component:User },
{ path:'/',redirect:'/home' }
]
// 路由实例
let router = new VueRouter({
mode:'history',
routes
})
// 导出路由实例,让他去控制vue根
export default router;
展示区
<router-view>展示区</router-view>
用来替换不同的pages
声明式跳转
理解
实现在页面点击跳转到不同的页面,不用在地址栏上跳转
语法
<router-link to="/home" tag='li' active-class='css类名'>声明式跳转</router-link>
注意
router-link 组件属性
tag='li' 指定编译后的标签
active-class='类名' 指定激活后的样式 模糊匹配
exact-active-class='类名' 指定激活后的样式 严格匹配router-link和router-view组件是vue-router插件提供的
重定向
理解
当访问到某一地址的时候,让它自动定向到指定地址上
语法
//src/plugins/router.js > routers
{
path: '/', //默认页
redirect: '/home' //配置型跳转
},
404
理解
路由的匹配是之上而下的,最后设置个任意地址都指向nopage页面
语法
{
path: '*',
component: NoPage组件
}
路由嵌套
语法
*{path: ':动态变量名',component: 组件名}
路由传参
理解
将?后面的参数传给子路由页面
语法
// 组件中
<router-link to='xx/参数?a=1&b=2'></..>
<router-link :to='{name:'名字',params:{id:参数},query:{a:2,b:3}}'></..>
命名路由
理解
给路由配置的绑定一个名字,使在动态传参的时候能匹配到该地址
语法
//src/plugins/router.js => routes
{path: '/home',component: Home, name:'名字'}, //route 一条路由的配置
组件接参
理解
接收组件中$route带的信息
语法
//template
{{$route.params/query/path}}
//script
this.$route.params/query
编程式跳转
理解
通过$router里的方法进行路由跳转
语法
this.$router.push(string|obj)
this.$router.push({name:'...'}) //添加一个路由 (记录到历史记录)
this.$router.replace({name:'...'}) //替换一个路由 (不记录到历史记录)
this.$router.go(-1|1)|back() //回退/前进
与声明式跳转的区别
声明式是通过组件标签进行跳转,而编程式是通过js进行跳转
路由模式
语法
// src/plugins/router.js
let router = new VueRouter({ //插件路由对象
routes,
mode:'hash'//哈希模式 location.href
mode:'history'//历史记录 history.pushState
});
路由元信息
理解
在$route中带一些数据,方便之后使用
语法
定义路由的时候配置'meta'字段
//src/plugins/router.js
{
path: '/home',
component: Home,
meta: { requiresAuth: true }
}
访问'meta'字段
this.$route.meta
to.meta from.meta
路由守卫
理解
对进入与离开设定一些权限和业务
全局守卫 位置
router.js
卫兵
前置
router.beforeEach((to, from, next) => {
next(false);//走不了
next(true);//走你
next('/login')//走哪
next({path:'/detail/2',params:{},query:{}})//带点货
// 守卫业务
if(to.path=='/login' || to.path=='/reg' || to.path=='/register'){
//判断是不是登录了
//axios请求 携带 token
next()
}else{
next('/login');
}
})
后置
router.afterEach((to,from)=>{
//全局后置守卫业务
})
参数
to: 目标路由 $route
from: 当前路由 $route
next() 跳转 一定要调用
路由独享守卫 概述
在注册路由的时候引用
只有前置
// src/plugins/router.js -> routes
{
path: '/user',
component: User,
beforeEnter: (to,from,next)=>{ //路由独享守卫 前置
console.log('路由独享守卫');
if(Math.random()<.5){
next()
}else{
next('/login')
}
}
},
组件内部守卫 概述
在组件内部的script里设置,例如弹出支付组件前要先判断你有没有登录
前置
beforeRouteEnter (to, from, next) {//前置 运行在beforeCreate 之前
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
}
后置
beforeRouteLeave (to, from, next) {//后置 运行在beforeDestory之前
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
组件被复用
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
}
滚动行为
理解
单页面只有一个滚动条,不同页面高度不一样,导致滚动条移动的固定百分比在不同的页面移动的像素距离是不同的
语法
// src/plugins/router.js
const router = new VueRouter({
scrollBehavior (to, from, savedPosition) {
//计算位置
return { x: 0, y: 0 }
}
})
其他
有这个符号代表引入router成功
思考
哈希模式和历史记录模式有什么区别?
格式不同,使用的api不同,兼容性不同
二十二、mock
1.概述
JSON-Server 是一个Node模块,运行Express服务器,你可以指定一个json文件作为api的数据源
2.基本操作
安装json-server
yarn add json-server -S
一个在前端本地运行,可以存储json数据的server
配置json文件的数据源
{
"course": [
{
"id": 1000,
"course_name": "马连白米且",
"autor": "袁明",
"college": "金并即总变史",
"category_Id": 2
},
{
"id": 1001,
"course_name": "公拉农题队始果动",
"autor": "高丽",
"college": "先了队叫及便",
"category_Id": 2
}
]
}
将数据源文件托管成一个web服务
二十三、mockjs库
1.概述
用mockjs里的动态方法生成模拟数据
2.使用
// 用mockjs模拟生成数据
var Mock = require('mockjs');
module.exports = () => {
// 使用 Mock
var data = Mock.mock({
'course|227': [
{
// 属性 id 是一个自增数,起始值为 1,每次增 1
'id|+1': 1000,
course_name: '@ctitle(5,10)',
autor: '@cname',
college: '@ctitle(6)',
'category_Id|1-6': 1
}
],
'course_category|6': [
{
"id|+1": 1,
"pid": -1,
cName: '@ctitle(4)'
}
]
});
// 返回的data会作为json-server的数据
return data;
};
二十四、状态管理
1.场景
多个组件间需要共享状态的时候
vuex
1.使用步骤
安装
npm i vuex@3.6.2 -S
引入
src/pulgins/vuex.js(注册vuex)
import Vue from 'vue';
import Vuex from 'vuex';
// 安装插件
Vue.use(Vuex);
import state from '../store/state';
let store = new Vuex.Store({
state
})
export default store;
src/store/state.js(创建仓库)
let state = {
count: 10
}
export default state
main.js(给vue引入仓库)
import store from './plugins/vuex'
new Vue({
render: h => h(App),
store
}).$mount('#app')
2.成员
vuex相关成员
状态管理store实例相关成员
3.角色分工
4.简易交互流程图
思考
用了vuex组件内部不用写业务,通过actions和getters来进行业务处理?那这两个算什么东西,另外开辟一个js文件?
另开辟js文件来处理
mapMutations是函数返回的是对象,包含mutations 里的add函数
二十五、组件通讯
1.贴层传递
子得父
$parent.父数据
(父更新,子更新)
父得子
$children[n].子数据
(子更新,父不会更新)
2.隔层传递
listeners 父传子
// A -> B... ->C->D
1. A中用属性绑定传值
2. B...C中都给下一紧贴层绑 v-band"$attres"
3. D中用props正常接收
子传父
// A <- B... <-C<-D
1. A中用@自定义事件="函数名"来接收D传来的数据
2. B...C中都给下一紧贴层绑 v-on"$listeners"
3. D中用$emit('自定义事件',数据)来发送数据
provide/inject 父传子
1. 父用provide:{}来提供变量
2. 子用inject:[]来接收变量
3.集中式管理
订阅发布模式 公共总线
//src//bus.js
import Vue from 'vue'
const bus = new Vue()
export default bus;
//组件内部
import bus from '...';
bus.$emit('事件',数据) //发布
bus.$on('事件',(接){处理}) //订阅
bus.$off('事件') //取消订阅
订阅要在发布之前
$root
web存储
通过把数据存储在客户端浏览器本地的行为,cookie、localstroage、session
状态管理
在浏览器下层,应用上层,打造一个"全局变量",利用vuex插件管理
数据库
利用本地,或者远端的数据库存储
二十六、动态组件
1.概述
让多个组件使用同一个挂载点,类似选项卡切换组件
2.语法
<component v-bind:is="which_to_show"></component>
data:{
which_to_show:'first'
},
components:{
first:{
template:'<div>这是子组件1<div>'
},
second:{
template:'<div>这是子组件2<div>'
},
third:{
template:'<div>这是子组件3<div>'
},
}
二十七、缓存组件
1.概述
- keep-alive 包裹了目标组件,对目标组件缓存,后期不会触发卸载挂载,但会触发actived/deactived
- keep-alive 不给属性时,默认内部出现过得组件,都会被缓存,子集无需包裹
2.语法
<keep-alive
:include="/组件名|组件名2/" 加入一部分
:exclude="['组件名','组件名2']" 排除一部分
:max = "数字" 最多可缓存的组件数,一旦这个数字达到了,时间戳最早出现的被卸载(遗忘)
>
..目标组件..
</keep-alive>
3.注意
其作用的钩子函数是,actived激活,deactived失活
二十八、组件懒加载
1.概述
拆分路由的加载时间
2.语法
// src/plugins/router.js 路由配置
{
path: '/home',
component: ()=>import("../pages/Login.vue")
}
持续更新中...