创建项目
npm create vue@latest
cd demo
npm install
npm run dev
插值语法
{{ name }}
指令语法
// 单向数据绑定
v-bind:href="url"
:href="url"
// 双向数据绑定
v-model:value = "name"
el和data的两种写法
第一种写法
const v = new vue({
el:"#root",
//data的第一种写法:对象式
data:{
name:"尚硅谷"
}
//data的第二种写法:函数式
data (){
return {
name: '尚硅谷'
}
}
})
第二种写法
v.$mount("#root")
MVVM模型
M:模型(Model),对应data中的数据
V:视图(View),模板
VM: 视图模型(ViewModel),Vue实例对象
vue中的数据代理
- vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
- vue中数据代理的好处:更加方便的操作data中的数据
- 基本原理:通过Object.defineProperty()把data对象中所有属性添加到vm上。为每一个添加到vm上的属性都指定一个getter/setter。在getter/setter内部去操作(读/写)data中对应的属性。
vue中的事件修饰符
prevent 阻止默认事件
stop 阻止事件冒泡
once 事件只触发一次
capture 使用事件的捕获模式
self 只有event.target是当前操作的元素时才触发事件
passive 事件的默认行为立即执行,无需等待事件回调执行完毕
键盘事件 keyup keydown
vue中常用的按键别名:
回车 enter
删除 delete
退出 esc
空格 space
换行 tab (特殊,必须配合keydown去使用)
上 up
下 down
左 left
右 right
e.key e.keyCode
Vue.cofig.keyCodes.自定义键名 = 键码, 可以去定制按键别名
computed 和watch之间的区别
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则:
1.所被vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
2.所有不被vue所管理的函数(定时器的回调函数、ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
绑定class样式
字符串写法, :class="mood" 适用于:样式的类名不确定,需要动态指定
数组写法,:class="[a,b,c]" 适用于:要绑定的样式个数不确定、名字也不确定
对象写法,:class="classObj" 适用于:要绑定的个数确定、名字也确定,但要动态决定用不用
条件渲染
1.v-if
写法:
(1) v-if="表达式"
(2) v-else-if="表达式"
(3) v-else="表达式"
适用于:切换频率较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if 可以和 v-else-if、v-else一起使用,但要求结构不能被“打断”。
2.v-show
写法:v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
3.备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
列表渲染
v-for指令
1.用于展示列表数据
2.语法:v-for="(item, index) in xxx" :key="yyy"
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
面试题:react、vue中的key有什么作用?(key的内部原理)
1.虚拟DOM中key的作用
key是虚拟DOM对象的标识,当状态中的数据发生变化时,vue会根据【新数据】生成的【新的虚拟DOM】,随后vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1)旧虚拟DOM中找到了新虚拟DOM相同的key:
1.若虚拟DOM中内容没变,直接使用之前的真实DOM!
2.若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到到页面。
-
用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 == >界面效果没问题,但效率低。
2.如果结构中还包含输入类的DOM:
会产生错误DOM更新 == >界面有问题。
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
开发中如何选择key ?:
1.最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,
使用index作为key是没有问题的。
收集表单数据
若 :< input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若 :< input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若 :< input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-mode1的初始值是非数组,那么收集的就是checked(勾选or 未勾选,是布尔值)
(2)v-mode1的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
v-cloak指令(没有值)
1.本质是一个特殊属性,vue实例创建完毕并接管容器后,会删掉v-cloak属性
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
v-once指令
1.v-once所在节点在初次动态渲染后 ,就视为静态内容了
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
v-pre
1.跳过其所在节点的编译过程
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
自定义指令
v-big-number="n"
'big-number'(){}
this都是window
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍
big函数何时会被调用?
1.指令与元素成功绑定时(一上来)2.指令所在的模板被重新解析时。
<div id="test5">
<h2>当前n的值是 <span v-text="n"></span></h2>
<h2>放大10倍后n的值是 <span v-big="n"></span></h2>
<button @click="n++">点我n+1</button>
</div>
<script>
export default {
data(){
return {
n:1
}
},
directives: {
big(element,binding){
element.innerText = binding.value * 10
}
}
}
</script>
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
<input type="text" v-fbind:value="n">
<script>
export default {
data(){
return {
n:1
}
},
directives: {
fbind: {
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
}
}
}
</script>
定义全局指令
Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
Vue.directive('big', function(element, binding){
element.innerText = binding.value * 10
})
生命周期
vue完成模板的解析并把真实的DOM元素放入页面后(挂载完毕)调用mounted
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子
2.是什么:vue在关键时刻帮我们调用的一些特殊名称的函数
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
4.生命周期函数中的this指向是vm或组件实例对象
beforeCreate 此时:无法通过vm访问到data中的数据、methods中的方法
created 此时:可以通过vm访问到data中的数据、methods中配置的方法
beforeMount 此时:1.页面呈现的是未经Vue编译的DOM结构 2.所有对DOM的操作,最终都不奏效
mounted 此时:1.页面中呈现的是经过Vue编译的DOM 2.对DOM的操作均有效(尽可能避免)。至此初始化过程结束,一般在此进行:开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作
beforeUpdate 此时:数据是新的,但页面是旧的,即:页面尚未和数据保持同步
updated 此时:数据是新的,页面也是新的,即:页面和数据保持同步
beforeDestroy 此时:vm中所有的:data、methods、指令等待,都处于可用状态,马上要执行销毁过程,一般在此阶段:关闭定时器、取消订阅消息、解绑自定义事件等收尾操作
destroyed
activated 路由组件被激活时触发
deactivated 路由组件失活时触发
nextTick
vue3中 beforeUnmount和unmounted代替beforeDestroy 和destroyed
常用的生命周期钩子:
1.mounted/发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy:清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会再beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
关于VueComponent:
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写<school/>或<school></school>,Vue解析时会帮我们创建schoo1组件的实例对象,
→即Vue帮我们执行的:new·VueComponent(options)。
3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent !!!!
4.关于this指向:
(1).组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【VueComponent实例对象】。
(2).new Vue()配置中:
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是【Vue实例对象】。
5.VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。
vue脚手架
Vue脚手架隐藏了所有webpace相关的配置,若想查看具体的webpace配置,请执行vue inspect > outputs.js
vue3中报错
ref属性
1.用来给元素或子组件注册引用信息(id的替代者)
2.应用在html标签上获取的是真是DOM元素,应用在组件标签上是组件实例对象(vc)
3.使用方式:
打标识: <h1 ref="xxx">....</h1> 或 <School ref="xxx"></School>
获取: this.$refs.xxx
配置项props
功能:让组件接收外部传过来的数据
(1).传递数据:
<Demo name="xxx"/>
(2).接收数据:
第一种方式(只接收):
props: ['name' ]
第二种方式(限制类型):
props : {
name : Number
}
第三种方式(限制类型、限制必要性、指定默认值):
props : {
name : {
type:String,//类型
required:true,//必要性
default:'老王’//默认值
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,
若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中
的数据。
mixin混入
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合,例如:
{
data(){ .... },
methods : { .... }
......
}
第二步使用混入,例如:
(1).全局混入:Vue.mixin(xxx)
(2).局部混入:mixins:['xxx']
vue插件
功能:用于增強Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数
据。
定义插件:
对象.install=function(Vue, options){
// 1. 添加全局过滤器
Vue.filter( .... )
// 2. 添加全局指令
Vue.directive( .... )
//3. 配置全局混入(合)
Vue.mixin( .... )
// 4. 添加实例方法
Vue.prototype. $myMethod = function () { ... }
Vue. prototype. $myProperty = xxxx
使用插件:Vue.use()
scoped样式
作用:让样式在局部生效,防止冲突。
写法:<style scoped>
浏览器本地存储
localStorage
<h2>localStorage</h2>
<button onclick="saveData()">点我保存一个数据</button>
<button onclick="readData()">点我读取一个数据</button>
<button onclick="deleteData()()">点我删除一个数据</button>
<button onclick="deleteAllData()">清空数据</button>
<script>
let p = {name:'张三',age:18}
function saveData(){
localStorage.setItem('msg','hello')
localStorage.setItem('person',JSON.stringify(p))
}
function readData(){
const p = localStorage.getItem('person')
console.log(JSON.parse(p))
}
function deleteData(){
localStorage.removeItem('msg')
}
function deleteAllData(){
localStorage.clear()
}
</script>
sessionStorage
与localStorage用法一样
sessionStorage存储的内容会随着浏览器窗口关闭而消失。
localStorage存储的内容需要手动清除才会消失
组件自定义事件绑定
this.$emit('事件名')
组件自定义事件解绑
this.off(['事件1','事件2']) //解绑多个自定义事件
this.$off() //解绑所有的自定义事件
全局事件总线:任意组件间通信
Vue.prototype.x = 111 报错,替换为
app.config.globalProperties.x = 111
消息订阅与发布
vue3使用 mitt插件 ,参考案例:如何在Vue3中使用事件总线_vue3事件总线-CSDN博客
1.订阅消息:消息名
2.发布消息:消息内容
nextTick
solt插槽 vue3写法
默认插槽
<slot></slot>
具名插槽
<slot name="center"></slot>
//v-slot:center 可简写为 #center
<template v-slot:center>
<img src="http://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</template>
旧写法
作用域插槽
<slot :game="game"></slot>
<games title="游戏" >
<template v-slot="atguigu">
<ul>
<li v-for="(item,index) in atguigu.game" :key="index">{{item}}</li>
</ul>
</template>
</games>
旧写法
vuex
概念:专门在vue中实现集中式状态(数据)管理的一个vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
state
mutations
actions
getters
modules
开启命名空间
namespaced:true,
路由
1.路由就是一组key-value的对应关系
2.多个路由,需要经过路由器的管理
vue-router的理解
vue的一个插件库,专门用来实现SPA应用心
对SPA应用的理解
- 单页Web应用(single page web application, SPA)。
- 整个应用只有一个完整的页面。
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
- 数据需要通过ajax请求获取。
路由的理解
什么是路由?
- 一个路由就是一组映射关系(key-value)
- key 为路径,value可能是function或componente
路由分类
- 后端路由:
- 理解:value是function,用于处理客户端提交的请求。
- 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处
理请求,返回响应数据。“
- 前端路由:
- 理解:value是component,用于展示页面内容。
- 工作过程:当浏览器的路径改变时,对应的组件就会显示。
多级路由(多级路由)
1.配置路由规则,使用children配置项:
routes : [
{
path:'/about',
component : About,
},
{
path: '/home',
component : Home, //通过children配置子级路由
children:[
{
path:'new', //此处一定不要写:/news
component : News,
}
{
path:'message',//此处一定不要写:/message
component :Message
}
]
}
]
2.跳转(要写完整路径):
<router-link to="/home/new9'>News</router-link>
路由的query参数
1.传递参数
<!-- 跳转并携带query参数,to的字符串写法 -- >
<router-link:to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -- >
<router-link
: to="{
path: '/home/message/detail',
query : {
id:666,
title:'你好'
}
>跳转</router-link>
2.接收参数:
$route.query.id
$route.query.title
命名路由
1.作用:可以简化路由的跳转
2.如何使用:
1.给路由命名:
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello',
path:'welcome',
component:Hello
}
]
}
]
}
2.简化跳转
//简化前,需要写完整的路径
<router-link to="/demo/test/welcome">跳转</router-link>
//简化后,直接通过名字跳转
<router-link :to="{name:'hello'}">跳转</router-link>
路由的params参数
1.配置路由,声明接受params参数
{
path:'/demo',
component:Demo,
children:[
{
path:'test',
component:Test,
children:[
{
name:'hello',
path:'welcome/:id/:title', //使用占位符声明接收params参数
component:Hello
}
]
}
]
}
2.传递参数
//跳转并携带params参数,to的字符串写法
<router-link :to="`/demo/test/welcome/${m.id}/${m.title}`">{{ m.title }}</router-link>
//跳转并携带params参数,to的对象写法
<router-link :to="{
name:'hello',
params:{
id:m.id,
title:m.title
}
}">{{ m.title }}</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
3.接收参数:
$route.params.id
路由的props配置
作用:让路由组件更方便的收到参数
{
name:'detail',
path:'detail/:id',
component:Detail,
//props的第一种写法,值为对象,该对象中的所有key-value都会以props的形式传递给detail组件
// props:{a:1,b:'hello'}
//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传递给detail组件
// props:true
//props的第三种写法,值为函数,该函数返回的对象中每一组key-value都会通过props传递给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
//解构赋值写法
props({query:{id,title}}){
return {id:id,title:title}
}
}
router-link的replace属性
1.作用:控制路由跳转时操作浏览器历史记录的模式
2.浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录,路由跳转时候默认为push
3.如何开启replace模式:<router-link replace······>News</router-link>
编程式路由导航
1.作用:不借助<router-link></router-link>实现路由跳转,让路由跳转更加灵活
2.具体编码:
this.$router.push({
name:'detail',
params:{
id:m.id,
title:m.title
}
})
this.$router.replace({
name:'detail',
params:{
id:m.id,
title:m.title
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进可后退
缓存路由组件
vue2写法
<keep-alive>
<router-view></router-view>
</keep-alive>
vue3写法
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component"/>
</keep-alive>
</router-view>
两个新的生命钩子
1.作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
2.具体名字:
activated 路由组件被激活时触发
deactivated 路由组件失活时触发
路由守卫
全局前置路由守卫 —— 初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to,from,next)=>{
if(to.meta.isAuth){ //判断是否需要鉴定权限
if(localStorage.getItem('school') == 'atguigu'){
next()
} else{
alert('学校名不对,无权限查看!')
}
} else{
next()
}
})
后置路由守卫 —— 初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to,from)=>{
document.title = to.meta.title || '硅谷'
})
独享路由守卫
beforeEnter:(to,from,next) => {
if(to.meta.isAuth){ //判断是否需要鉴定权限
if(localStorage.getItem('school') == 'atguigu'){
next()
} else{
alert('学校名不对,无权限查看!')
}
} else{
next()
}
}
组件内路由守卫
//通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next){
if(to.meta.isAuth){ //判断是否需要鉴定权限
if(localStorage.getItem('school') == 'atguigu'){
next()
} else{
alert('学校名不对,无权限查看!')
}
} else{
next()
}
},
//通过路由规则,离开该组件时被调用
beforeRouteLeave(to,from,next){
next()
}
history模式与hash模式
1.对于一个url来说,什么是hash值 ?- #及其后面的内容就是hash值。
2.hash值不会包含在HTTP请求中,即:hash值不会带给服务器。
3.hash模式:
1.地址中永远带着#号,不美观。
2.若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
3.兼容性较好。
4.history模式:
1.地址干净,美观。
2.兼容性和hash模式相比略差。
3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
nodejs +express 部署
1.创建nodeDemo文件夹
-
开启终端,执行 npm init
起名之后一直回车,
3.npm install express
4.安装成功后新建文件 server.js
image.png
const express = require('express')
const app = express()
app.get('/person',(req,res)=>{
//函数体
res.send({
name:'Tom',
age:18
})
})
app.listen(5005,(err)=>{
if(!err) console.log("服务器启动成功了!")
})
5.node server
启动服务器,访问页面可得到人的信息
- 新建static文件夹,index.html页面
修改server.js
app.use(express.static(__dirname+'/static'))
Document
直接访问网址可得到index.html的内容
7.将项目打包好的dist文件里的内容复制到static文件夹里面,就可以访问项目
由于是history模式,点击页面切换之后刷新 页面会报错,
改成hash模式之后重新打包,启动服务,项目访问不会报错。
如果非得用history模式打包的话,通过安装中间件来访问404页面
npm install connect-history-api-fallback
安装完之后在server.js上引入
const history = require('connect-history-api-fallback')
Vue UI 组件库
移动端常用UI组件库
- Vant 介绍 - Vant Weapp (gitee.io)
- Cube UI cube-ui Document (didi.github.io)
- Mint UI Mint UI (mint-ui.github.io)
PC端常用 UI 组件库
- Element UI Element - 网站快速成型工具
- IView UI iView / View Design 一套企业级 UI 组件库和前端解决方案 (iviewui.com)
3.Ant Design Vue 组件总览 - Ant Design Vue (antdv.com)