一.前端工程化
webpack
代码压缩、解决js 的浏览器兼容性问题、性能优化
webpack.config.js文件下:
mode 节点:
1.development
开发时用,不代码压缩和性能优化,打包速度块
2.production
发布时用,代码压缩和性能优化,打包速度慢
webpack.config.js文件是webpack 的配置文件,打包前先读取这个配置文件,从而基于给定的配置,对项目打包
默认的打包入口文件 src -> index.js
通过entry 节点指定打包的入口文件
默认的输出文件路径 dist -> main.js
通过uotput 节点指定打包的出口文件
npm run dev 进行打包
插件
1.webpack-dev-server
每当修改了代码,webpack自动进行项目的打包和构建
打包生成的文件不再放到dist 文件夹下,而是存放到了内存中,默认是放到了项目的根目录中
2.html-webpack-plugin
自定制index.html 页面的内容,并赋值一份index.html放到项目根目录下
之前自动注入了打包的bundle.js
节点:
mode 节点:
1.development
开发时用,不代码压缩和性能优化,打包速度块
2.production
发布时用,代码压缩和性能优化,打包速度慢
entry 指定打包的入口文件
output 指定打包的出口文件
*** plugin 挂载插件的实例对象
devServer
自动打开浏览器,指定主机地址,指定端口号
module
配置第三方文件模块的规则
*** loader
打包非.js后缀名结尾的模块
style-loader
css-loader .css
less-loader .less
babel-loader 打包webpack无法处理的高级JS语法
url-loader file-loader url路径相关的文件
devtool
保证运行时报错的行数与源代码的行数保持一致
为什么要打包发布:
开发环境下,打包生成的文件存放于内存中,无法获取最终打包生成的文件
开发环境下,打包生成的文件不会进行代码压缩和性能优化
*** Source Map 文件
文件中存储着代码压缩混淆前后的对应关系,可以定位到错误行并显示对应的源码
可以配置devtool 保证运行时报错的行数与源代码的行数保持一致
二.Vue3
1.过滤器:(vue 2.x 中使用 v3没有)
常用于文本的格式化
在filters 节点下定义过滤器函数
格式:
待处理的数据 | 过滤器函数
用在两个地方:
插值表达式 和 v-bind 属性绑定
全局过滤器:
调用vue 构造函数所提供的filter 方法
Vue.filter('过滤器函数',(str)=>{
return str.charAt(0).toUpperCase() + str.slice(1)
})
还可以连续调用多个过滤器:
{{ message | 过滤器1 | 过滤器2 }}
2.MVVM
M 数据源data
V 视图 el 所指向的区域
vm 是new 出来的VUE实例对象
3.组件
1.单页面应用程序(SPA):
缺点:
1.首屏加载慢
解决方法:路由懒加载、代码压缩、CDN加速、网络传输压缩
2.不利于SEO
解决方法:SSR服务器端渲染
2.创建vue 的SPA项目
1.vite方法
vue3.x,不基于webpack,运行速度块,小而巧
2.vue-cli
vue3.x 和 2.x,基于webpack,运行速度慢,大而全
3.vite的使用
npm init vite-app 项目名称
cd 项目名称
npm install
npm run dev
vue的作用:通过main.js 把App.vue 根组件的内容渲染到index.html 中
4.组件的基本使用
1.全局注册组件
1.在main.js先导入全局注册的组件
import Swiper from './components/globalRge/Swiper.vue'
调用app.component() 方法来全局注册组件
app.component('my-swiper',Swiper)
2.使用标签的形式使用全局组件
2.局部注册组件
在组件内部导入需要注册的组件
1.import Search from './components/globalRge/Search.vue'
2.components:{
'my-search':Search
}
3.
尽量使用大驼峰命名法来命名组件:MyTest
3.scoped 和 :deep() v3中 /deep/ v2中(父组件可以影响子组件的样式) [属性选择器] .title
4.props 是组件的自定义属性
props的作用:父组件通过props 向子组件传递要展示的数据
好处:提高了组件的复用性
5.父组件向子组件传值:(props 自定义属性)
在子组件中:
{{title}} {{author}}
props: ['title','author']
在父组件中:
import MyArticle from './Article.vue'
components:{
MyArticle
},
data(){
return {
info{
title: 'a',
author: 'b'
}
}
}
1.动态的添加和移除class 类名
data(){
return {
classObj:{
italic:false,
delete:flase
}
}
}
2.以对象语法绑定内联的style
data(){
return{
active:'red',
fsize:30,
bgcolor:'pink'
}
}
组件总结:
1.什么是单页面应用程序以及组件化开发
SPA、只有一个页面、组件是对UI结构的复用
2..vue单文件组件的组成部分
template、script、style(scoped、lang)
3.如何注册vue 组件
全局注册(app.component)局部注册(components)
4.如何声明组件的props 属性
props 数组
5.如何在组件中进行样式绑定
动态绑定class、动态绑定style
组件基础下:
1.props 验证
使用对象类型的props 节点,可以对每个prop 进行数据类型的校验
props:['count','state']
对比:
props:{
count:Number,
state:[Number,String] // 可以指定多个类型
name:{
type:String,
default: 100 // 用户没有指定值,则默认为100
required:true // 当前属性的值是必填项
}
}
自定义验证函数:
props:{
name:{
validator(value){ // “属性的值”通过形参value 进行接收
return ['success','warning','danger'].indexOf(value)!==-1
// 属性的值匹配这些字符串中的一个,true 表示验证通过
}
}
}
2.计算属性
计算属性就是一个function 函数,可以实时监听data 中数据的变化,并return一个计算后的新值,供组件渲染DOM时使用
{{count}} 乘以 2 的值为: {{plus}}
data(){
return {count:1}
},
computed:{
plus(){ // 计算属性:监听data 中count 值的变化,自动计算出count*2 之后的新值
return this.count *2
}
}
注意:
1.计算属性必须定义在computed 节点中
2.计算属性必须是一个function 函数
3.计算属性必须有返回值
4.计算属性必须当做普通属性使用 如:{{plus}}
相对于方法来说,计算属性会缓存计算的结果,只有计算属性的依赖项发生变化时,才会重新进行计算
因此计算属性的性能更好
computed:{
isDisabled(){
return this.total === 0 // 0 代表禁用
}
}
3.自定义事件(子组件向父组件传值)
1.
myheader 子组件中:
export default{
emits:['change'], // 1.声明自定义事件
methods:{
onBtnClick(){
this.$emit('change') // 2.触发自定义事件
}
}
}
App.vue 父组件中:
methods:{
getCount(){
console.log('监听到了 count 值的变化')
}
}
2.
// 触发自定义事件
export default{
name:'MyCounter',
emits:['countChange'],
data(){
return{
count:0
}
},
methods{
add(){
this.count++
this.$emit('countChange')
}
}
}
父组件中:
methods:{
getCount(){
console.log('触发了countChange 自定义事件')
}
}
3.如何传参
// 触发自定义事件
export default{
name:'MyCounter',
emits:['countChange'],
data(){
return{
count:0
}
},
methods{
add(){
this.count++
this.$emit('countChange',this.count) // 通过第二个参数传值
}
}
}
父组件中:
methods:{
getCount(val){
console.log('触发了countChange 自定义事件',val) // 通过形参接收
}
}
4.在组件上使用v-model 实现父子组件的双向数据绑定
父向子同步数据:
父组件中:
子组件中:
props:['number']
子向父同步数据:
父组件中:
子组件中:
emits:['update:number']
methods:{
add(){
this.$emit('update:number',this.number+1)
}
}
1.watch 侦听器
1.
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作
export default{
data(){
return {username:''}
},
watch:{
username(newVal,oldVal){
console.log(newVal,oldVal)
}
}
}
await 和 async 简化异步操作,从而得到真正的数据对象
2.
监听username 值的变化,并使用axios 发起Ajax 请求,检测当前输入的用户名是否可用:
import axios from 'axios'
export default{
data(){
return { username: ''}
},
watch:{
async username(newVal,oldVal){ // username 是一个方法
const {data:res} = await axios.get('https://www.escook.cm/api/finduser/'+newVal)
console.log(res)
}
}
}
3.immediate选项
默认情况下,组件在初次加载完毕后不会调用watch 侦听器,如果想让watch 侦听器立即被调用,则需要使用immediate选项
watch:{
// 1.监听username 值的变化
username:{ // username 指向一个对象
// 2.handler 属性是固定写法,当username 变化时,调用handler
async handler(newVal,oldVal){
const {data:res} = await axios.get('https://escook.cn/api/finduser/${newVal}')
console.log(res)
},
// 3.表示组件加载完毕后立即调用一次当前的watch 侦听器
immediate: true
}
}
4.deep 选项
当watch 侦听的是一个对象,如果对象中的属性发生了变化,则无法被监听到,此时需要使用deep 选项
data(){
return{
info:{
username:'zs',
age: 20
}
}
},
watch:{
info:{
async handler(newVal,oldVal){
const{ data: res } = await axios.get('https://www.escook.cn/api/finduser/'+newVal.username)
console.log(res)
},
deep:true
// 监听info 对象中每个属性的变化,只要有属性值发生了变化,就执行watch
}
}
5.监听对象中单个属性的变化
data(){
return{
info: {username:'admin',password:''}
}
},
watch:{
'info.username':{ // 指向监听info.username 属性值的变化
async handler(newVal,oldVal){
const{ data:res } = await axios.get('http://www.escook.cn/api/finduser/'+newVal.username)
console.log(res)
}
}
}
计算属性和侦听器的区别:
计算属性侧重于监听多个值的变化,最终计算并放回一个新值
侦听器侧重于监听单个数据的变化,最终执行特定的业务处理,不需要有任何返回值
2.组件的生命周期
组件的运行过程:
开始 - import导入组件 - components注册组件 - 以标签形式使用组件 - 在内存中创建组件的实例对象 - 把创建的组件实例渲染到页面上 - 组件切换时销毁需要被隐藏的组件 - 结束
组件的生命周期:指的是组件的 创建 - 运行 - 销毁 的整个过程,强调的是一个时间段
组件的生命周期函数:
beforeCreate // 在内存中开始创建组件之前, 创建阶段, 唯一一次
created() // 组件在内存中被创建完毕了
// 创建阶段 唯一一次 最早的发ajax 请求初始数据
beforeMount // 在组件初次渲染到页面之前,创建阶段,唯一一次
mounted() // 组件第一次被成功渲染到了页面上
// 创建阶段 唯一一次 最早的操作DOM 元素
beforeUpdate // 在组件被重新渲染之前 运行阶段,0或多次
updated() // 组件被重新渲染完毕了
// 当组件的data 数据更新之后,vue 会自动重新渲染组件的DOM 结构,从而保证View 视图展示的数据和Model 数据源保持一致
// 当组件被重新渲染完毕之后,会自动调用updated 生命周期函数
// 运行阶段 0或多次
beforeUnmount // 在组件被销毁之前 销毁阶段,唯一一次
unmounted() // 组件被销毁完毕了
// 销毁阶段 唯一一次
为什么不在beforeCreate 中发起ajax 请求初始数据?
在拿到ajax 返回回来的数据之后,需要存到data 里面去,才能够在组件渲染的时候访问到data里面的数据
但是在beforeCreate 中是无法访问到data 里面的数据的,即使请求了数据,但是请求的数据无法挂载到data中,共组件使用
因此最早只能在created 里面发起ajax 请求
能否在beforeMount 里面操作DOM 元素?
不可以,因为在mounted 执行的时候,才第一次把当前组件渲染到页面中
3.组件之间的数据共享
父子关系,兄弟关系,后代关系
1.父子组件之间的数据共享
1.父组件向子组件共享数据 属性绑定
父组件通过v-bind 属性绑定向子组件共享数据,同时,子组件需要通过props 接收数据
父组件中:
data(){
return {
message: 'hello vue',
userinfo:{name:'zs',age:20}
}
}
子组件中:
{{msg}}
{{user}}
export default{
props:['msg','user']
}
2.子组件向父子间共享数据
子组件通过自定义事件的方式向父组件共享数据
子组件中:
export default{
emits:['n1change'], // 1.声明自定义事件
data(){
return {n1:0}
},
methods:{
addN1(){
this.n1++,
// 2.数据变化,并触发自定义事件
this.$emit('n1change',this.n1)
}
}
}
父组件中:
// 1.监听子组件的自定义事件 n1change
export default{
data(){return { n1FromSon:0 }},
methods:{
getn1(n1){ // 2.通过形参,接收子组件传递过来的数据
this.n1FromSon = n1
}
}
}
3.父子组件之间的数据双向同步
父组件中:
data(){
return {
count: 0
}
}
子组件中:
props:['num'],
emits:['update:num'],
methods:{
add(){
this.$emit('update:num',this.num+1)
}
}
2.兄弟组件之间的数据共享
兄弟组件之间实现数据共享的方案是EventBus,可以借助第三方的包mitt 来创建eventBus 对象
npm i [email protected] -S
//创建一个eventBus.js
// 导入mitt 包
import mitt from 'mitt'
// 创建EventBus 的实例对象
const bus = mitt()
// 将EventBus 的实例对象共享出去
export default bus
数据接收方:
import bus from './eventBus.js'
created(){
bus.on('countChange',(count) => {
this.num = count
})
}
数据发送方:
import bus from './eventBus.js'
data(){
return {
count: 0
}
}
methods:{
add(){
this.count++
bus.emit('countChange',this.count)
}
}
3.后代组件之间的数据共享
指的是父节点的组件向其子孙组件共享数据
可以使用provide 和inject 实现后代关系组件之间的数据共享
父节点的组件可以通过provide 方法,对其子孙组件共享数据
export default{
data(){
return { color:red }
},
provide(){
return {
color: this.color
}
}
}
子孙节点的组件可以通过inject 数组,接收父级节点向下共享的数据
export default{
// 子孙组件,使用inject 接收父节点向下共享的color 数据,并在页面上使用
inject:['color']
}
父节点向外共享响应式的数据:
结合computed 函数向下共享响应是的数据:
如果父组件共享的是响应式的数据,则子孙节点必须以.value 的形式进行使用
父组件中:
import { computed } from 'vue'
export default{
data(){
return { color: 'red' }
},
provide(){
return{
// 使用computed 函数,可以把要共享的数据包装成 响应式的数据
color: computed(() => this.color)
}
}
}
子孙组件中:
export default{
inject: ['color']
}
4.vuex
vuex 是组件之间实现数据共享的方案,可以让组件之间的数据共享变得高效、清晰、且易于维护
4.v3 中全局配置axios
在main.js 入口文件中,通过app.config.globalProperties 全局挂载axios
1.安装axios
npm i axios -S
2.main.js 中
import axios from 'axios'
const app = createApp(App)
// 挂载请求的根路径
axios.defaults.baseURL = 'https://www.esbook.cn'
// 全局挂载自定义属性 $http
axios.config.globalProperties.$http = axios
app.mount('#app')
3.发起post 请求
methods:{
async postInfo(){
const {data:res} = await this.$http.post('/api/post',{name:'zs',age:20})
console.log(res)
}
}
4.发起get 请求
methods:{
async getInfo(){
cosnt {data:res} = await this.$http.get('/api/get',{
params:{
name: 'ls',
age: 33
}
})
console.log(res)
}
}
5.购物车案例
1.ref 引用
ref 是用来辅助开发者在不依赖于jQuery 的情况下,获取DOM 元素或组件的引用
每个vue 的组件实例上,都包含一个$refs 对象,里面存储着对应的DOM 元素或组件的引用
默认情况下,组件的$refs 指向一个空对象
示例:
methods:{
getRefs(){
console.log(this.$refs.myh3) // this.$refs.myh3 就指向
}
}
可以通过为子组件设置ref,调用该组件内部的方法
父组件中:
methods:{
getRefs(){
this.$refs.counterRef.reset()
}
}
子组件中:
methods:{
reset(){
this.count = 0
}
}
组件的DOM 元素是异步的进行更新渲染
而代码是同步执行的,当需要的DOM 元素没有被及时渲染时,ref 获取的是undefined
2.this.$nextTick(cb)方法
组件的$nextTick(cb) 方法,会把cb 会调推迟到下一个DOM 更新周期之后执行
等组件的DOM 异步地重新渲染完成后,再执行cb回调函数,从而能保证cb 回调函数可以操作到最新的DOM 元素
示例:
methods:{
showInput(){
this.inputVisible = true
// 把对 input 文本框的操作,推迟到下次DOM 更新之后,否则页面上根本不存在文本框元素
this.$nextTick(() => {
this.$refs.ipt.focus()
})
}
}
2.动态组件
动态组件指的是动态切换组件的显示与隐藏
vue 中提供了一个内置的
1.
2.通过is 属性动态指定要渲染的组件名称
3.
示例:App 根组件中有Home 和Movie 两个组件
//
//
data(){
return{
comName:'MyHome'
}
},
components:{
MyHome,
MyMovie
}
动态组件切换时,默认情况下,不会记录组件中数值的变化
因为动态组件在切换时,是对组件的重新创建和销毁
3.插槽
插槽(Slot) 是vue 为组件的封装者提供的能力,允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽
可以把插槽认为是组件封装期间,为用户预留的内容的占位符
没有预留插槽的内容会被丢弃
示例:
子组件中:
111
333
父组件中: 222 默认
2.具名插槽
v-slot: 可以简写成#
子组件中:
父组件中:
标题
// 标题
尾部
// 尾部
3.作用域插槽
在封装组件的过程中,可以为预留的
{{ info }} // scope 是自定义的,是用来接收数据的,是一个大对象,里面包含了插槽传过来的数据
4.自定义指令
1.私有自定义指令
在每个vue组件中,可以在directives 节点下声明私有自定义指令
directives:{
// 自定义一个私有指令
focus:{
// 当被绑定的元素插入到DOM 中时,自动触发mounted 函数
mounted(el){ // el 表示当前指令所绑定到的DOM 元素
el.focus() // 让被绑定的元素自动获得焦点
}
}
}
mounted()函数, 表示被绑定到的DOM 元素渲染到页面中时,自动触发该函数
2.全局自定义指令
在main.js 中
const app = createApp(App)
app.directives('focus',{
mounted(el){
el.focus()
}
})
app.mount('#app')
3.updated 函数
mounted 函数只在元素第一次插入DOM 时被调用,当DOM 更新时mounted 函数不会被触发
updated 函数会在每次DOM 更新完成后被调用
app.directives('focus',{
mounted(el){ // 第一次插入DOM 时触发这个函数
el.focus()
},
updated(el){ // 每次DOM 更新都会触发 updated 函数
el.focus()
}
})
注意点:
1.
v3 中的mounted 在v2 中是bind
v3 中的updated 在v2 中是update
2.当mounted 和updated 函数中的代码一样时可以简写
app.directives('focus',(el) => {
// 在mounted 和updated 时都会触发相同的业务处理
el.focus()
})
4.指令的参数值
在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值
示例:
app.directives('color',(el,binding) => {
// binding.value 就是通过"等号"为指令绑定的值
el.style.color = binding.value
})
5.Table 案例
一.路由
路由就是对应关系
前端路由 指的是: Hash 地址与组件之间的对应关系
v2 用的是vue-router 3.x
v3 用的是vue-router 4.x
vue-router
步骤:
1.在项目中安装vue-router
npm i vue-router@next -S
2.定义路由组件
3.声明路由链接和占位符
声明路由链接:
路由占位符:
4.创建路由模板
1.创建router.js 路由模块
2.import { createRouter,createWebHashHistory } from 'vue-router'
import Home from './MyHome.vue'
import Movie from './MyMovie.vue'
import About from './MyAbout.vue'
// 3.创建路由实例对象
const router = createRouter({
// 3.1通过history 属性指定路由的工作模式
history: createWebHashHistory(),
// 3.2 通过routes 数组,指定路由规则
routes: [
// path 是hash 地址,component 是要展示的组件
{path:'/home',component:Home},
{path:'/movie',component:Moive},
{path:'/about',component:About},
]
})
export default router
5.导入并挂载路由模块
在main.js 中
import router from './router.js'
const app = createApp(App)
// 挂载路由模块
app.use(router)
app.mount('#app')
vue-router 的高级用法:
1.路由重定向
const router = createRouter({
history: createWebHashHistory(),
routes: [
{path:'/',redirect:'/home'},
{path:'/home',component:Home},
{path:'/movie',component:Moive},
{path:'/about',component:About},
]
})
2.高亮路由
1.默认的高亮class 类
被激活的路由链接,默认会应用一个叫做router-link-active 类名
可以使用此类名选择器,为激活的路由链接设置高亮的样式
// 在index.css 全局样式表中
.router-link-active{
background-color: red;
color:white;
font-weight:bold;
}
2.自定义路由高亮的calss 类
在创建路由的实例对象时,可以基于linkActiveClass 属性,自定义路由链接被激活时所应用的类名
const router = createRouter({
history: createWebHashHistory(),
// 指定被激活的路由链接,会应用router-avtive 这个类名
// 默认的router-link-active 类名会被覆盖掉
linkActibeClass: 'active-router',
routes: [
{path:'/',redirect:'/home'},
{path:'/home',component:Home},
{path:'/movie',component:Moive},
{path:'/about',component:About},
]
})
3.嵌套路由
步骤:
1.在router.js 模块中,导入需要的组件,并使用children 属性声明子路由规则
import Tab1 from './components/tabs/MyTab1.vue'
import Tab2 from './components/tabs/MyTab2.vue'
const router = createRouter({
routes:[
{
path:'/about',
conponent:About,
redirect: '/about/tab1', // 嵌套路由的重定向
children:[
{path:'tab1',component:Tab1},
{path:'tab2',component:Tab2},
]
}
]
})
2.在about.vue 组件中
// 路由占位符
4.动态路由匹配
动态路由指的是:把Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性,
在vue-router 中使用英文的冒号 : 来定义路由的参数项
示例:
// 路由中的动态参数以 : 进行声明,冒号后面的是动态参数的名称
{ path:'/movie/:id',component:Movie }
// 将以下三个路由规则,合成为一个,提高了路由规则的复用性
{ path:'/movie/1',component:Movie }
{ path:'/movie/2',component:Movie }
{ path:'/movie/3',component:Movie }
5.获取动态路由参数值
1.使用$router.params 获取参数值
示例:
{ path:'/movie/:id',component:Movie }
$router.params.id
2.使用props 接收路由参数
{ path:'/movie/:id',component:Movie,props: true} // 为当前组件开启了动态路由传参
{{ id }}
export default{
props: ['id']
}
6.编程式导航
通过调用API 实现导航的方式,叫做编程时导航
通过点击链接实现导航的方式,叫做声明式导航
例如:
通过点击链接、vue项目中点击
调用location.href 跳转到新页面的方式,属于编程式导航
vue-router 中的编程式导航API
1.this.$router.push('hash地址')
跳转到指定Hash 地址,从而展示对应的组件
示例:
methods:{
goToMovie(id){
this.$router.push('/movie/'+id)
}
}
2.this.$router.go(数值 n)
实现导航历史的前进、后退
示例:
methods:{
goBack(){
this.$router.go(-1) // 后退到上一个组件页面
}
}
7.命名路由
通过name 属性为路由规则定义名称的方式,叫做命名路由
注意:命名路由的name 值不能重复,必须保持唯一性
1. 使用命名路由实现声明式导航
{
path:'movie/:id',
// 使用name 属性为当前的路由规则定义一个"名称"
name:'mov',
component:Movie,
props:true
}
2. 使用命名路由实现编程式导航
methods:{
goToMovie(id){
this.$router.push({ name:'mov', paramas: { id: 3 }})
}
}
8.导航守卫
1.全局导航守卫
全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制
示例:
// 创建路由实例对象
const router = createRouter({...})
// 调用路由实例对象的beforeEach 函数,声明“全局前置守卫”
// fn 必须是一个函数,每次拦截到路由的请求,都会调用fn 进行处理
// 因此 fn 叫做“守卫方法”
router.beforeEach(fn)
2.全局导航守卫的三个形参
router.beforeEach((to,from,next) => {
// to 目标路由对象
// from 当前导航正要离开的路由对象
// next 是一个函数,表示放行
})
注意:
1.在守卫方法中,如果不声明next 形参,则默认允许用户访问每一个路由
2.在守卫方法中,如果声明了next 形参,则必须调用next()函数,否则不允许用户访问任何一个路由
next 函数的三种调用方式:
1.直接放行 next()
router.beforeEach((to,from,next) => {
if(to.path === '/main'){
// 证明用户要访问后台主页
next()
}else{
// 访问的不是后台主页
next()
}
})
2.强制其停留在当前页面:next(false)
router.beforeEach((to,from,next) => {
if(to.path === '/main'){
// 停留在/main
next(false)
}else{
next()
}
})
3.强制其跳转到指定的页面: next('/login')
router.beforeEach((to,from,next) => {
if(to.path === '/main'){
// 强制访问登录页面
next('/login')
}else{
next()
}
})
3.结合token 控制后台主页的访问权限
// 全局路由导航守卫
router.before((to,from,next) => {
const tokenStr = localStorage.getItem('token')
if(to.path === '/main' && !tokenStr){
next('/login')
}else{
next()
}
})
二.综合案例
1.vue-cli
vue 脚手架,是vue 官方提供的、快速生成vue 工程化项目的工具
特点:
1.开箱急用
2.基于webpack
3.功能丰富且易于扩展
4.支持创建vue2和v3的项目
安装:
npm i -g @vue/cli
vue create my-project
项目解构:
node_modules 存放的是第三方的依赖包
index.html 是项目唯一的首页
src 是程序的源代码
assets 存放静态资源
components 存放组件
App.vue 是项目的根组件
main.js 是项目的入口文件
package.json 是包管理配置文件
在v2 中使用3.x 的路由:
1.npm i [email protected] -S
2.在components 下 新建 Home.vue 和 movie.vue
3.在 src -> router -> index.js 中创建路由模块
import Vue from 'vue' // 1.导入vue2 的构造函数
import VueRouter from 'vue-router' // 2.导入3.x 路由的构造函数
import Home from '@/components/Home.vue' // 3.导入需要使用路由切换的组件
import Movie from '@/components/Movie.vue'
// @ 就代表src 这层目录
Vue.use(VueRouter) // 4.调用Vue.use() 函数,把路由配置为Vue 的插件
const router = new VueRouter({ // 5.创建路由对象
routes: [ // 6.声明路由规则
{path:'/',redirect:'/home'},
{path:'/home',component: Home},
{path:'/movie',component:Movie}
]
})
export default router // 7.向外共享路由对象
4.在main.js 中导入路由模块
import Vue from 'vue'
import App from './App.vue'
// 1.导入路由模块
import router from './router'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
// 2.挂载路由模块
// router : router
router,
}).$mount('#app')
2.vue 组件库
pc端:
element ui
View ui
移动端:
Mint ui
Vant
1.element UI
1.V2中使用的是 element ui
npm i element-ui -S
// 在main.js 中
import ElementUI from 'element-ui'
import 'element-ui/lis/theme-chalk/index.css'
Vue.use(ElementUI) // 把ElementUI 注册为 vue 的插件
2.v3中使用的是 element plus
3.proxy 跨域代理
如果后端没有开启CORS 跨域资源共享的情况下
通过代理解决接口的跨域问题
如:
vue 项目运行在 http://localhost:8080
API 接口项目 在 https://www.escook.cn
1.把axios 的请求根路径设置为vue 项目的运行地址
axios.defaults.baseURL = 'http://localhost:8080'
2.vue 项目发现请求的接口不存在,把请求转交给proxy 代理
3.代理把请求根路径替换为derServer.proxy 属性的值,发起真正的数据请求
代理把请求到的数据转发给axios
this.$http.get('/api/users')
proxy 代理:
module.exports ={
devServer:{
proxy: 'https://www.escook.cn'
}
}