目录
8-1 webpack相关
//安装webpack
npm i webpack webpack-cli -D
//在根目录创建webpack.config.js的webpack配置
module.exports={
mode:'development'//构建模式,development/production
}
//在 package.json 的 scripts 节点下,新增 dev 脚本如下:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack"
}
//在终端中运行 npm run dev 命令,启动 webpack 进行项目的打包构建
1.mode节点的可选值
mode 节点的可选值有两个,分别是:
① development
⚫ 开发环境
⚫ 不会对打包生成的文件进行代码压缩和性能优化
⚫ 打包速度快,适合在开发阶段使用
② production
⚫ 生产环境
⚫ 会对打包生成的文件进行代码压缩和性能优化
⚫ 打包速度很慢,仅适合在项目发布阶段使用
2.默认约定(可以在webpack.config.js中修改)
在 webpack 4.x 和 5.x 的版本中,有如下的默认约定:
① 默认的打包入口文件为 src -> index.js
② 默认的输出文件路径为 dist -> main.js
3.webpack中的插件
① webpack-dev-server
⚫ 类似于 node.js 阶段用到的 nodemon 工具
⚫ 每当修改了源代码,webpack 会自动进行项目的打包和构建
npm install webpack-dev-server@3.11.2 -D
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack serve"
}
② html-webpack-plugin
⚫ webpack 中的 HTML 插件(类似于一个模板引擎插件)
⚫ 可以通过此插件自定制 index.html 页面的内容
npm install html-webpack-plugin@5.3.2 -D
4.webpack中的loader
在实际开发过程中,webpack 默认只能打包处理以 .js 后缀名结尾的模块。其他非 .js 后缀名结尾的模块, webpack 默认处理不了,需要调用 loader 加载器才可以正常打包,否则会报错。
loader 加载器的作用:协助 webpack 打包处理特定的文件模块。
比如:
⚫ css-loader 可以打包处理 .css 相关的文件
⚫ less-loader 可以打包处理 .less 相关的文件
⚫ babel-loader 可以打包处理 webpack 无法处理的高级 JS 语法
8-2 基础入门
1.特性
1.1 数据驱动视图
在使用了 vue 的页面中,vue 会监听数据的变化,从而自动重新渲染页面的结构。
好处:当页面数据发生变化时,页面会自动重新渲染。
注意:数据驱动视图是单向的数据绑定。
1.2双向数据绑定
在填写表单时,双向数据绑定可以辅助开发者在不操作 DOM 的前提下,自动把用户填写的内容同步到数据源中。
好处:开发者不再需要手动操作 DOM 元素,来获取表单元素最新的值
1.3MVVM
MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel
在 MVVM 概念中:
- Model 表示当前页面渲染时所依赖的数据源。
- View 表示当前页面所渲染的 DOM 结构。
- ViewModel 表示 vue 的实例,它是 MVVM 的核心。
1.4vue的基本使用
指定时不要用标签指定,只会处理第一个。最好要用id指定。
2.vue的指令与过滤器
2.1内容渲染指令
内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 3 个:
v-text 【会覆盖元素的默认值】
<p v-text="gender">性别</p>
{{ }} 【插值】
<p>性别:{{gender}}</p>
v-html【可渲染标签】
info:'<p>元素</p>'
<p v-html="info"></p>
2.2属性绑定指令
v-bind/:
tips:'please input...'
<input type="text" v-bind:placeholder:"tips"/>
<input type="text" :placeholder:"tips"/>
tips:
在 vue 提供的模板渲染语法中,除了支持绑定简单的数据值之外,还支持 Javascript 表达式的运算。
{{2>3?'yes':'no'}}
<div :id="'list'+id"></div>
2.3事件绑定指令
v-on/@
method:{
add:function(n){
console.log(vm.count+n)
add(n){//推荐写法
console.log(this.coutn+n)
f(e){//event,简写为e
e.target.style.backgroundColor='red'
}
}
}
<button v-on:click="add(2)">按钮</button>
<button @click="add(2)">按钮</button>
注意:原生 DOM 对象有 onclick、oninput、onkeyup 等原生事件,替换为 vue 的事件绑定形式后, 分别为:v-on:click、v-on:input、v-on:keyup
2.4$event
e v e n t 是 v u e 提供的特殊变量,用来表示原生的事件参数对象 e v e n t 。 event 是 vue 提供的特殊变量,用来表示原生的事件参数对象 event。 event是vue提供的特殊变量,用来表示原生的事件参数对象event。event 可以解决事件参数对象 event 被覆盖的问题
<button @click="add(2,$event)">按钮</button>
f(n,e){//event,简写为e
e.target.style.backgroundColor='red'
}
2.5修饰符
-
事件修饰符
在事件处理函数中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。因此, vue 提供了事件修饰符的概念,来辅助程序员更方便的对事件的触发进行控制。常用的 5 个事件修饰符如下:
<a href="#" @click.prevent="add">连接</a>
-
按键修饰符
<a href="#" @keyup.enter="submit">连接</a>
2.6双向绑定指令
v-model:在不操作 DOM 的前提下,快速获取表单的数据.
<p>1:{{ password }}</p>
<input type="text" v-model="password">
相似的,value是单向数据绑定,数据源的改变才能引起页面的改变。
为了方便对用户输入的内容进行处理,vue 为 v-model 指令提供了 3 个修饰符,分别是:
2.7条件渲染指令
条件渲染指令用来辅助开发者按需控制 DOM 的显示与隐藏。
- v-show
- v-if (最常用)
- v-else-if
- v-else(必须和v-if一起用)
flag:true
<p v-show="flag==true">true</p>
<p v-if="score==A">A</p>
<p v-else-if="score==B">B</p>
<p v-else>C</p>
两者区别:
实现原理不同:
- v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏;
- v-show 指令会动态为元素添加或移除 style=“display: none;” 样式,从而控制元素的显示与隐藏;
性能消耗不同:
v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此:
- 如果需要非常频繁地切换,则使用 v-show 较好
- 如果在运行时条件很少改变,则使用 v-if 较好
2.8列表渲染指令
v-for:基于一个数组来循环渲染一个列表结构。
v-for 指令需要使 用 item in items 形式的特殊语法,其中:
- items 是待循环的数组
- item 是被循环的每一项
data:{
list:[
{id:1,name:'zs'},
{id:2,name:'ls'}
]
}
<ul>
<li v-for="item in list">id:{{ item.id }} 姓名:{{ item.name }}</li>
</ul>
<li v-for="(item,index) in list">索引:{{ index }} 姓名:{{ item.name }}</li>
</ul>
item,index是形参,可以改为其他,如user,i
使用 key 维护列表的状态(建议用到v-for就用key)
当列表的数据变化时,默认情况下,vue 会尽可能的复用已存在的 DOM 元素,从而提升渲染的性能。但这种 默认的性能优化策略,会导致有状态的列表无法被正确更新。
为了给 vue 一个提示,以便它能跟踪每个节点的身份,从而在保证有状态的列表被正确更新的前提下,提升渲 染的性能。此时,需要为每项提供一个唯一的 key 属性:
<ul>
<li v-for="item in list" :key="item.id">姓名:{{ item.name }}</li>
</ul>
注意事项:
① key 的值只能是字符串或数字类型
② key 的值必须具有唯一性(即:key 的值不能重复)
③ 建议把数据项 id 属性的值作为 key 的值(因为 id 属性的值具有唯一性) 主键
④ 使用 index 的值当作 key 的值没有任何意义(因为 index 的值不具有唯一性)
⑤ 建议使用 v-for 指令时一定要指定 key 的值(既提升性能、又防止列表状态紊乱)
2.9过滤器
过滤器(Filters)是 vue 为开发者提供的功能,常用于文本的格式化。过滤器可以用在两个地方:插值表达式{{}} 和 v-bind 属性绑定。
2.9.1定义过滤器
<!--插值表达式{{}}-->
<p>{{ username | capitalize}}</p>
<!--v-bind 属性绑定-->
<div v-bind:id="rawID | formatId"></div>
filters:{//过滤器一定要有返回值
capitalize(val){//形参中的val是前面的值,即username
//过滤器作用:首字母大写
const first=val.charAt(0).toUpperCase()
const other=val.slice(1)
return first+other
}
}
注意:本质是个函数,要写在filters下,要有返回值
2.9.2私有/全局过滤器
-
私有过滤器
在 filters 节点下定义的过滤器,称为“私有过滤器”,因为它只能在当前 vm 实例所控制的 el 区域内使用。
-
全局过滤器
Vue.filter("capitalize",function(val){ const first=val.charAt(0).toUpperCase() const other=val.slice(1) return first+other })
2.9.4连续调用多个过滤器(串联调用)
2.9.5过滤器传参
过滤器的本质是 JavaScript 函数,因此可以接收参数。
第一个参数是管道符前面的待处理值。
tips:vue3已经不支持过滤器了
3.侦听器
watch 侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。
方法格式的侦听器:要监听的数据名作为方法名即可。
data:{
username:'admin'
},
watch:{
username(new,old){//新,旧
console.log(new,old)
}
}
无法在刚进入页面的时候自动触发;
如果侦听的是对象,对象中的属性变化侦听不到
对象格式的侦听器
data:{
info:{username:'admin',password:'123'}
},
watch:{
username:{
handler(new){
console.log(new.username)
}
}
}
可以通过immediate和deep解决以上缺点。
3.1immediate属性
默认情况下,组件在初次加载完毕后不会调用 watch 侦听器。如果想让 watch 侦听器立即被调用,则需要使 用 immediate 选项。示例代码如下:
watch: {
username: {
// handler 是固定写法,表示当 username 的值变化时,自动调用 handler 处理函数
handler: async function (newVal) {
if (newVal === '') return
const { data: res } = await axios.get('https://www.escook.cn/api/finduser/' + newVal)
console.log(res)
},
// 表示页面初次渲染好之后,就立即触发当前的 watch 侦听器
immediate: true
}
}
3.2deep属性
如果 watch 侦听的是一个对象,如果对象中的属性值发生了变化,则无法被监听到。此时需要使用 deep 选项。
const vm=new Vue({
el:'#app',
data:{
info:{username:'admin',password:'123'}
},
watch:{
info:{
handler(new){
console.log(new.username)
},
deep:true
}
}
})
3.3侦听对象单个属性
watch:{
'info.username':{
handler(new){
console.log(new)
}
}
}
4.计算属性
计算属性指的是通过一系列运算之后,最终得到一个属性值。
这个动态计算出来的属性值可以被模板结构或 methods 方法使用。
① 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性
② 计算属性会缓存计算的结果,只有计算属性依赖的数据变化时,才会重新进行运算
5.vue-cli
安装:
npm install -g @vue/cli
5.1vue-cli的使用
在终端中:
vue create 项目名称
cd 项目名称
npm run serve
vue 要做的事情:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中。
① App.vue 用来编写待渲染的模板结构
② index.html 中需要预留一个 el 区域
③ main.js 把 App.vue 渲染到了 index.html 所预留的区域中
6.vue组件
.vue为后缀
每个 .vue 组件都由 3 部分构成,分别是:
- template -> 组件的模板结构
- script -> 组件的 JavaScript 行为
- style -> 组件的样式
其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分
6.1template
- template 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素
- template 中只能包含唯一的根节点
6.2script
<script>//data、methods等
export default {//固定写法
data:{//data 必须是一个函数,不能直接指向一个数据对象
info:{username:'zhangsan'},
//count:0 错误写法
}
}
</script>
6.3style
<style lang="less">//lang="less"属性可以使用less语法
h1 {//普通语法
font-weight:normal;
}
h1{//less语法
font-weight:normal;
span{
color:red;
}
}
</style>
6.4使用组件的步骤
6.5全局/私有组件
通过components注册的组件是私有的。
例如: 在组件 A 的 components 节点下,注册了组件 F。 则组件 F 只能用在组件 A 中;不能被用在组件 C 中。
全局组件:
在 vue 项目的 main.js 入口文件中,通过 Vue.component() 方法,可以注册全局组件
6.6props
props 是组件的自定义属性,在封装通用组件的时候,合理地使用 props 可以极大的提高组件的复用性。
<script>
export default {
props:['init','自定义属性2'],
data() {
return {}
},
};
</script>
vue 规定:组件中封装的自定义属性是只读的,程序员不能直接修改 props 的值。否则会直接报错。
可以把props的值转存到data中
data(){
count:return this.init
}
8-3 生命周期&数据共享
1.生命周期&生命周期函数
2.组件之间的数据共享
-
父子共享
-
父->子(自定义属性)
类似与props里面是指针。
-
子->父(自定义事件)
-
-
兄弟共享(EventBus)
① 创建 eventBus.js 模块,并向外共享一个 Vue 的实例对象
② 在数据发送方,调用 bus.$emit(‘事件名称’, 要发送的数据) 方法触发自定义事件
③ 在数据接收方,调用 bus.$on(‘事件名称’, 事件处理函数) 方法注册一个自定义事件
3.ref引用
ref 用来辅助开发者在不依赖于 jQuery 的情况下,获取 DOM 元素或组件的引用。
每个 vue 的组件实例上,都包含一个 $refs 对象,里面存储着对应的DOM 元素或组件的引用。默认情况下, 组件的 $refs 指向一个空对象。
3.1引用dom元素
<h1 ref="myh3">ref引用dom元素</h1>
this.$refs.myh3.style.color='red'
ps:引用组件实例也可以用此方法。
3.2引用组件实例
<my-counter ref="counterRef"></my-counter>
methods:{
getRef(){
console.log(this.$refs.counterRef)//可以使用组件实例
this.$refs.counterRef.add()//也可以调用组件上的methods方法
}
}
3.3控制文本框和按钮的按需切换
3.4文本框自动获得焦点
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加 ref 引用,并调用原生 DOM 对象的 .focus() 方法即可。
3.5this.$nextTick(cb) 方法
组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。
通俗的理解是:等组件的 DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。
8-4 动态组件 & 插槽 & 自定义指令
1.动态组件
动态组件指的是动态切换组件的显示与隐藏。
1.1动态组件渲染
vue提供了一个内置的组件,专门用来实现动态组件的渲染。
<component is="组件名称"></component>
<!--例-->
<component :is="comName"></component>
<script>
data(){
return comName:"Left"
}
</script>
1.2使用keep-alive保持状态
默认情况下,切换动态组件时无法保持组件的状态。(data值等)
<keep-alive>
<component is="组件名称"></component>
</keep-alive>
1.3keep-alive对应的生命周期函数
组件第一次创建:created和activated都会被触发
组件被缓存:自动触发deactivated生命周期函数
组件被激活:自动触发activated生命周期函数
组件被销毁:destoyed
<script>
export default{
created(){ console.log('创建') },
destroyed(){ console.log('销毁') },
activated(){ console.log('激活') },
deactivated(){ console.log('缓存') }
}
</script>
1.4keep-alive的include/exclude属性
include 属性用来指定:只有名称匹配的组件会被缓存。(默认keep-alive包裹的所有组件都会被缓存)
多个组件名之间使用英文的逗号分隔。
<keep-alive include="Left,Right">
<component :is="comName"></component>
</keep-alive>
exclude属性则表示排除项,include/exclude不能同时使用。
2.插槽
插槽(Slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的 部分定义为插槽。
2.1插槽基础
在封装组件时,可以通过元素定义插槽,从而为用户预留内容占位符。
-
如果在封装组件时没有预留任何插槽,则用户提供的任何自定义内容都会被丢弃。
-
封装组件时,可以为预留的 插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。
2.2具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个插槽指定具体的 name 名称(属性)。这种带有具体名称的插槽叫做“具名插槽”。
在向具名插槽提供内容的时候,我们可以在一个元素上使用v-lot指令提供名称,也可以简写为#
<slot name="content"></slot>
<template v-lot=content>
<h3>内容</h3>
</template>
<template #content>
<h3>内容</h3>
</template>
如果没有指定名称,则所有插槽都会显示用户自定义内容。
2.3作用域插槽
在封装组件的过程中,可以为预留的 插槽绑定 props 数据,这种带有 props 数据的 叫做“作用域插槽”
<tbody>
<slot v-for="item in list" :user="item"></slot>
</tbody>
可以使用 v-slot: 的形式,接收作用域插槽对外提供的数据。scope是形参,没有意义。
2.4结构插槽
作用域插槽对外提供的数据对象,可以使用解构赋值简化数据的接收过程。
3.自定义指令
vue 官方提供了 v-text、v-for、v-model、v-if 等常用的指令。除此之外 vue 还允许开发者自定义指令。
3.1定义私有自定义指令(directives节点)
定义:
<script>
export default{
directives:{
color:{
bind(el){
el.style.color="red"
}
}
},
}
</script>
3.2使用自定义指令
使用:在使用自定义指令时,需要加上 v- 前缀
<h1 v-color>
红色
</h1>
//动态绑定参数值
data(){
return{
color:"red"
}
}
<h1 v-color="color">
红色
</h1>
3.3通过binding获取指令的参数值
在声明自定义指令时,可以通过形参中的第二个参数,来接收指令的参数值:
directives:{
color:{
bind(el.binding){
el.style.color=binding.value
}
}
}
3.4update函数
bind 函数只调用 1 次:当指令第一次绑定到元素时调用,当 DOM 更新时 bind 函数不会被触发。 update 函 数会在每次 DOM 更新时被调用。
如果 insert 和update 函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式:
3.5全局自定义指令
全局共享的自定义指令需要通过“Vue.directive()”进行声明
8-5 路由
1.前端路由
Hash 地址与组件之间的对应关系。
1.1工作方式
2.vue-router的基本用法
① 安装 vue-router 包
npm i vue-router@3.5.2 -S
② 创建路由模块
在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:
//路由模块
//1.导包
import Vue from 'vue'
import VueRouter from 'vue-router'
//2.调用Vue.use()函数,把VueRouter安装为Vue的插件
Vue.use(VueRouter)
//3.创建路由实例对象
const router=new VueRouter()
//4.向外共享路由实例对象
export default router
③ 导入并挂载路由模块
在 src/main.js 入口文件中,导入并挂载路由模块
import Vue from 'vue'
import App from './App.vue'
//导入路由模块
import router from './router'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
//2.挂载路由模块
router:router
}).$mount('#app')
④ 声明路由链接和占位符
在 src/App.vue 组件中,使用 vue-router 提供的 和 声明路由链接和占位符
//3.创建路由实例对象
const router=new VueRouter({
routes:[//声明路由的匹配规则
//path(要匹配的hash地址):component(要展示的路由组件)
{path:'/home',component:Home},
{path:'/movie',component:Movie}
]
})
3.vue-router 的常见用法
3.1路由重定向
用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。
通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向
//3.创建路由实例对象
const router=new VueRouter({
routes:[//声明路由的匹配规则
//当用户访问/的时候,跳转到/home对应的路由规则
{path:'/',redirect:'/home'}
//path(要匹配的hash地址):component(要展示的路由组件)
{path:'/home',component:Home},
{path:'/movie',component:Movie}
]
})
3.2嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由(右)。
3.3子路由链接和子路由占位符
在 About.vue 组件中,声明 tab1 和 tab2 的子路由链接以及子路由占位符:
在 src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:
3.4动态路由匹配
动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。 在 vue-router 中使用英文的冒号(:)来定义路由的参数项。
- 可以使用 this.$route.params 对象访问到动态匹配的参数值
-
vue-router 允许在路由规则中开启 props 传参。
3.5声明式导航 & 编程式导航
在浏览器中,点击链接实现导航的方式,叫做声明式导航。
普通网页中点击 链接、vue 项目中点击 都属于声明式导航
在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。
普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:
① this.$router.push(‘hash 地址’)
跳转到指定 hash 地址,并增加一条历史记录
② this.$router.replace(‘hash 地址’)
跳转到指定的 hash 地址,并替换掉当前的历史记录
③ this.$router.go(数值 n)
实现导航历史前进、后退
this.$router.go简化:
① $router.back()
在历史记录中,后退到上一个页面
② $router.forward()
在历史记录中,前进到下一个页面
3.6 导航守卫
导航守卫可以控制路由的访问权限。
3.6.1全局前置首位
每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行 访问权限的控制:
3.6.2守卫方法的三个形参
3.6.3next 函数的 3 种调用方式
3.6.4控制后台主页的访问权限
8-6 补充
1.在main.js中
import axios from axios
axios.defaults.baseURL='请求根路径'
Vue.prototype.$http=axios
请求时: