Unit 01
一. 介绍
1.是一个构建数据驱动的==渐进式 JavaScript==框架
- 数据驱动: 基本不用操作dom, 只要修改数据, 数据变化了, dom会自动渲染
- 渐进式: 学一点用一点,不必学完所有的东西才使用, 只要学习其中的某一个部分,都可以集成到你的项目中
- vue特点
- 体积小
- 运行效率高
- 双向数据绑定
- 生态丰富 学习成本低
二. vue项目环境搭建
- 安装脚手架
yarn global add @vue/cli
# 或
npm i -g @vue/cli
# 测试脚手架版本
vue --version // @vue/cli 4.5.13
- 创建项目
vue create 项目名 // 项目名: 小写加-
vue create vite
# 一顿选择 [看图形笔记]
- 启动项目
yarn serve
三. 项目目录说明
四. 项目初始代码详解
- main.js
import Vue from 'vue' // 引入Vue
import App from './App.vue' // 引入根组件 ***************
Vue.config.productionTip = false // 阻止生产提示
// 创建Vue的实例对象 (证明: Vue是一个类哦~~~)
new Vue({
render: h => h(App), // 渲染哪个组件呢?
}).$mount('#app') // 挂载渲染到哪里去
// new Vue({
// render: function (createElement) {
// return createElement(App) // 把App.vue根组件 编译成浏览器认识的东西 渲染
// }, // 渲染哪个组件呢?
// }).$mount('#app') // 挂载渲染到哪里去
- App.vue
<template>
<div class="title">写html的</div>
</template>
<script>
console.log("写js的");
</script>
<style>
.title {
color: red;
}
</style>
五. 组件
1. 组件的概念
- 具有独立功能的ui部件, 组件的特点是: 可复用/可组合/容易维护
2. 单文件组件
- 一个.vue文件, 就是一个单文件组件
<template>
// 这里是写HTMl模板的, 主要就是写 - 布局
# 注意:
1. 只能有一个根标签
2. 主要写 "html模板", html + vue扩展的写法
</template>
<script>
// 这里写js交互
// 导出一个vue的实例对象 export default new Vue({})
// 这个实例对象 会 自动和 template 关联
export default {
// 这里有很多vue "内置" 的配置
// 数据
data() {
retrun {
}
},
// 方法
methods: {
},
// 组件
components: {
},
// 计算属性
computed: {
}
}
</script>
<style scoped lang="less">
// 这里写css样式
# 注意:
1. style的样式 自动和当前组件的 template 关联
2. scoped 可以方style中的css 只对当前组件的template有效
3. lange="less" 那么 就可以再style中写less了.
</style>
3. 自定义组件
- 自己写的.vue组件
- 使用步骤 (3步骤)
- 写一个自定义组件,然后在要使用这个组件的地方,引入组件
- 注册组件
- 使用组件
# 编写组件 HelloWorld.vue
# 使用步骤
1. 引入组件
import 变量名 from './xx/HelloWorld.vue'
2. 注册组件
export default {
// 用于注册组件的
components: {
"键名": 变量名
# 注意:
1. 键名如果使用大驼峰 在使用组件的时候 只能使用 小写+"-" 格式
}
}
3. 使用组件
<template>
<div>
<键名></键名> 或 <键名 />
</div>
</template>
六. data选项&mustach表达式
1. data选项
- data是,vue实例对象中用于准备数据的内置选项, 这些数据,在 template 中, 可以直接使用. 且: data中的数据,具备==响应式==, 只要数据改变, template中使用到这个数据的地方 自动重新渲染, 无需dom操作
2. mustach表达式
- 作用是用于在 template 中, 输出渲染 data选项提供的数据
{{ 表达式 }}
<template>
<div>
<h3>{{ str }}</h3>
<p>我的爱好是: {{ hob[1] }}</p>
<p>{{ num1 + num2 }}</p>
<p>{{ flag ? "真的哦" : "假的哦" }}</p>
<p>我的公司的地址是: {{ o.company.address }}</p>
</div>
</template>
<script>
// export default new Vue({})
export default {
// 数据 必须是函数 然后返回一个对象
data() {
return {
str: "你好vue",
hob: ["html", "css", "vue"],
num1: 10,
num2: 20,
flag: true,
o: {
name: "小貂蝉",
company: {
address: "天府三街",
},
},
};
},
};
</script>
<style lang="less" scoped>
</style>
七. 指令
1. 概念
- 指令是指我们可以写在 template 中的标签上, 作为自定义属性, 以
v-开头 - 作用是: 帮助我们操作dom节点 和 操作属性节点 , 渲染数据
<template>
<div>
<标签名 id="box" class="xx" v-xx="表达式"></标签名>
</div>
</template>
1.1 v-text 和 v-html
- 往标签中渲染数据, v-text相当于 textContent , v-html 相当于 innerHTML
1.2 v-show和v-if
- 都可以控制dom的显示和隐藏, 只是: v-show控制的是 display属性, v-if会直接删除dom和重新渲染
- 区别: 频繁的切换显示和隐藏, 使用 v-show , 否则 使用if
1.3 v-if和v-else-if和v-else
- 逻辑和js的if else 完全一样. 从上往下执行, 遇到==第一个满足条件的== 就渲染 , 然后就不走了
1.4 v-for
- 循环渲染数据
<template>
<div>
<div>
<!-- 循环数字 -->
<span v-for="i in num" :key="i">{{ i }}</span>
</div>
<div>
<!-- 循环字符串 -->
<p v-for="(str, index) in strs" :key="index">{{ str }}</p>
</div>
<div>
<!-- 循环数组 -->
<h1>强哥的爱好:</h1>
<ul>
<li v-for="(hob, index) in hobs" :key="hob">
{{ index }} --- {{ hob }}
</li>
</ul>
</div>
<div>
<!-- 循环对象 -->
<h1>强哥的对象:</h1>
<ul>
<li v-for="(value, key, index) in user" :key="key">
{{ index }} --- {{ key }}--- {{ value }}
</li>
</ul>
</div>
<!-- 循环数组对象 -->
<table>
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.age }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
// 数据
data() {
return {
num: 5,
strs: "hello",
hobs: ["钓鱼", "洗脚", "按摩", "丝袜奶茶"],
user: {
name: "小貂蝉",
age: 20,
sex: "男",
email: "dc@qq.com",
},
users: [
{ id: 1, name: "张三", age: 18 },
{ id: 2, name: "李四", age: 19 },
{ id: 3, name: "王五", age: 20 },
{ id: 4, name: "赵六", age: 21 },
{ id: 5, name: "田七", age: 22 },
],
};
},
};
</script>
<style lang="less" scoped>
</style>
1.5 v-model
- 帮我们 获取 和 设置 表单数据的, 让 template中的表单 和 data中的数据 "双向绑定". 完全保持一致,同步修改
- 能使用的表单标签: input select textarea
<template>
<div>
<h1>vip信息录入</h1>
<p>姓名: <input type="text" v-model="user.name" /></p>
<p>
性别: 男 <input type="radio" name="sex" value="男" v-model="user.sex" />
女
<input type="radio" name="sex" value="女" v-model="user.sex" />
</p>
<p>
爱好: 钓鱼 <input v-model="user.hobs" type="checkbox" value="钓鱼" /> 洗脚
<input v-model="user.hobs" type="checkbox" value="洗脚" /> 按摩
<input v-model="user.hobs" type="checkbox" value="按摩" /> 丝袜奶茶
<input v-model="user.hobs" type="checkbox" value="丝袜奶茶" />
</p>
<p>
工作:
<select v-model="user.job">
<option value="web开发">web开发</option>
<option value="php开发">php开发</option>
<option value="py开发">py开发</option>
</select>
</p>
<p>
简介:
<textarea cols="30" rows="10" v-model="user.desc"></textarea>
</p>
<p><input type="checkbox" v-model="agree" /> 同意授权信息</p>
<p><button @click="submitForm">提交</button></p>
</div>
</template>
<script>
export default {
// 准备数据
data() {
return {
// 用户数据
user: {
name: "aa",
sex: "",
hobs: [],
job: "web开发",
desc: "",
},
// 是否同意
agree: false,
};
},
// 方法
methods: {
submitForm() {
// console.log("提交", this.agree, this.user); // this代表当前组件的vue实例对象
if (this.agree) {
console.log("提交", this.user); // 发送ajax给后端
}
},
},
};
</script>
<style lang="less" scoped>
</style>
1.6 v-bind
属性名:
标准属性: 操作方式: dom.属性 dom.属性=" "
自定义属性: 操作方式: dom.getAttruite('属性') dom.setAttruite('属性名','新值')
h5新增 自定义数据: data-key="值" js操作: dom.dataset['key'] dom.dataset.key
div
- id className style width height
input
- type placeholder value checked
a
- href
img
- src alt title
textarea
- row col ..
<template>
<div>
<div v-bind:id="num" v-bind:class="classData" v-bind:xxx="num" v-bind:data-id="num"></div>
<input type="text" :placeholder="msg">
<button @click="add">+</button>
</div>
</template>
<script>
export default {
data(){
return {
num:0,
classData:'box',
msg:'请输入数据'
}
},
methods:{
add(){
this.num++;
this.classData = this.classData=='box'?'box1':'box'
this.msg = this.msg=='请输入数据'?'你好啊':'请输入数据'
}
}
}
</script>
<style lang="less" scoped>
.box{
width: 100px;
height: 100px;
background: red;
}
.box1{
width: 100px;
height: 100px;
background: green;
}
</style>
1.7 v-on 事件指令 和methods 执行函数
- v-on绑定事件
- methods 事件的执行函数
<template>
<div>
<div class="btn" v-on:mouseover="over" v-on:mouseout="out">点我呀....</div>
</div>
</template>
<script>
export default {
data() {
return {};
},
components: {},
// 方法和函数的定义
methods: {
over() {
console.log('移入了~~~~');
},
out() {
console.log("移出了~~~~");
}
}
};
</script>
八. 事件
- Vue绑定事件语法
<template>
<div>
<button v-on:事件类型="表达式">点我啊</button>
<button v-on:事件类型="事件处理函数">点我啊</button>
<button v-on:事件类型="事件处理函数(实参)">点我啊</button>
// 简写
<button @事件类型="表达式">点我啊</button>
<button @事件类型="事件处理函数">点我啊</button>
<button @事件类型="事件处理函数(实参)">点我啊</button>
</div>
</template>
九. 计算属性computed
-
computed写法和methods一模一样,主要用于写复杂的计算逻辑, 然后返回结果, 在 template中, 通过函数的名字,就能直接使用结果 -
区别
-
methods调用要写() 计算属性直接写函数名
-
methods函数不一定要有返回值 计算属性必须有返回值.
-
计算属性有 "依赖缓存", 只要依赖的数据没有发生变化, 就会把结果缓存起来, 下一次直接使用结果, 不用重新计算, 性能极高
<template>
<div>
{{ 函数名 }}
</div>
</template>
export default {
// 计算属性
computed: {
函数名() {
/* 很多的逻辑 计算代码 */
return 结果
}
}
}
十. 过滤器filters
- 过滤数据 并返回新的数据
- filters 语法与 methods 一样,必须跟一个返回值
- 区别:
- filters 函数 必须有return, filters 函数不能获取data变量的值
- methods 的函数可以没有return
<img v-for="(item,index) in data" :key="index" :src="item.img | 过滤器名 "> </img>
<script>
export defalut{
data(){
return {
data:[
{img:"/xx/xx.png"},
{img:"/xx/xx.png"},
{img:"/xx/xx.png"},
{img:"/xx/xx.png"}
]
}
},
filters:{
过滤器名(形参){
//字符拼接 并返回
retrun '服务器地址'+形参
}
}
}
</script>
<p v-for="item in arr" :key="item">
{{item | addStr}}
</p>
十一. 侦听器 watch
- 当数据 发生改变时,自动触发侦听器中的函数, 监听某个数据是否发生变量,如果数据发生了变化 就执行某些功能
- watch 语法和methods 一致, watch中的函数名 直接就是 需要监听的数据变量名
<input v-model="num" >
<script>
export default{
data(){
return {
num:0
}
},
watch:{
需要监听的变量名(新数据,老数据){){
执行的函数体
}
}
}
</script>
watch:{
//当num的值发生改变 触发该函数
num(newVal,oldVal){
console.log(newVal,oldVal);
}
}
-
watch 和 computed 区别
都可以: 监听一个数据的改变 然后 返回一个新数据
watch 的函数 监听一个数据的改变 然后 返回一个新数据
watch 在 data中定义一个 变量接收新数据,才可以使用
computed 监听一个数据的改变 然后 返回一个新数据
自己的函数名 就是 返回的变量 代表的是 新数据
computed 的语法 要比 watch 的语法 更加 简洁
-
methods 和computed 区别
都可以当做函数的调用
methods 可以接收 形参
computed 不能接收形参,他自己就是一个变量
computed 依赖缓存 只要参与计算的数据源 不发生改变,计算函数的函数体 不会执行,但是methods 会一直执行,computed 性能更高。
Unit 02
一. vue-router vue路由
- 需要实现 类似于之前页面跳转的效果,达到vue组件相互切换的效果,需要引入vue路由实现
- 路由的配置项文件: src/router/index.js
import Vue from 'vue' // 引入vue
import VueRouter from 'vue-router' //引入vue-router
Vue.use(VueRouter) //vue使用 vuerouter
// 页面路由的配置
// vue组件的路径 和 路由访问的地址 一一对应
const routes = [
//每个页面的配置 都是一个对象,
{
path:'', // 路由访问的地址
name:'', //路由名称
component:'' // vue组件路径
},
{
path:'', // 路由访问的地址
name:'', //路由名称
component:'' // vue组件路径
}
]
//实例化 vuerouter 对象
const router = new VueRouter({
routes
})
//导出 在main.js 导入路由配置 就可以生效了
export default router
- router的属性
this.$route.path #vue 组件的路径 路由
this.$route.params #获取组件上面的参数
this.$route.query #获取vue组件上面的查询查询 ?key=value&key1=value1
this.$router.push('组件的路由path属性') #通过js跳转组件
this.$router.go(数字) #根据历史记录跳转组件 -----history.go()
this.$eouter.back() #根据历史记录 回退组件 -----history.back()
this.$router.forward() #根据历史记录 前进一页 ----history.forward()
二. vue 组件生命周期
- 所有的生命周期的钩子函数,当组件到达这个阶段 会进行自动的触发
- 所有的生命周期的钩子函数都是和 data()函数同级
#组件创建阶段
# 组件创建之前
beforeCreate(){
#组件实例被创建,但是 页面data 的数据 都是undefiend
}
# 组件创建之后 【一般进入页面请求ajax 数据 都在created 】
created(){
#所有的数据生效, 页面data 的数据 都可以获取到值
}
# 组件挂载阶段
#挂载前
beforeMount(){
#页面dom节点即将进行渲染,获取dom节点===null
}
#挂载后 【操作dom 一般是在 mounted】
mounted(){
#所有dom节点 及数据进行了双向绑定,页面完成了数据的渲染,获取dom可以直接拿到dom节点
}
# 组件的更新阶段
# 更新前
beforeUpdate(){
#数据一旦发生更改,自动触发,可以获取到该数据更改之前的值
}
#更新后
updated(){
#数据发生改变触发,可以获取数据更改之后的值
}
#组件销毁阶段
#销毁前 【离开需要关闭的其他异步操作,定时器 延时器 需要在此清除】
beforeDestory(){
#组件被销毁/关闭 跳转到下个组件(跳转前的一瞬间)进行触发
}
#销毁后
destoryed(){
#组件被销毁/关闭 跳转到下个组件(完成跳转的一瞬间)。进行触发
}
三. 页面级组件的创建
- 页面路由配置的 口诀
1创 :创建一个 页面级组件 views/
2.配 : 配置 router/index.js 加入这个页面相关的配置
3.占位 : 找到这个页面的父页面 占位 < router-view> </ router-view> 【同级的配置只要占位一次】
4.测 :在浏览器的地址栏 输入 该页面的路由地址 path的值 进行访问测试
//router/index.js 配置
//导入 vue
import Vue from 'vue';
import VueRouter from 'vue-router';
import Discover from '@/views/discover/Discover.vue'
//顶级类 注册 子类
Vue.use(VueRouter);
//2.配置
const routes = [
{
path:'/discover',
component:Discover
}
]
//实例化 VueRouter
let router = new VueRouter({
routes,//配置项
})
//暴露实例对象
export default router;
//main.js
import router from '@/router/index.js'
//挂载到当前项目的实例上
new Vue({
router:router,
})
- 占位 app.vue
<div class="app">
<AppHeader></AppHeader>
<!-- 占位 -->
<router-view></router-view>
</div>
- 测试
http://localhost:8080/#/discover
四. 组件传值
1. 父传子
- 父组件使用了子组件,子组件没有参数需要父组件将参数传递过来
- 父组件使用子组件时,直接以自定义属性的方式 写入参数
#父组件
# 参数1="值1" 只能传递字符串
<子组件 参数1="值1" 参数2=“值2” :参数3="表达式"> </子组件>
#子组件
props:{
参数1:{
type:Number, //限制传递数据类型
default:0, // 默认值
}
}
# 子组件通过props 获取的数据 都可以像data中的数据一样进行使用
props:{
val:{
type:Number,
default:0,
},
arr:{
type:Array,
default:()=>{ //如果接受的数据引用数据类型,默认值需要写一个函数返回空对象
return [];
}
}
},
2.子传父
- 将子组件的值 传递给父组件
#父组件
事件执行函数名 后面不需要加 ()
<子组件 @自定义事件类型名="事件执行函数名" > </子组件>
methods:{
事件执行函数(data){
//data ===子组件回传的数据
}
}
#子组件
this.$emit('自定义事件类型名',回传的参数)
3. 中央事件总线 $bus
- 将跨层级 的传值 放到中央事件总线中
- 同一个页面中 跨域层级的组件相互之间值的传递
// 在 main.js 中 创建中介
// 重新实例化一个 空的 vue对象(中介) 挂载到 现在正在使用的这个vue对象的 原型链上
Vue.prototype.$bus = new Vue();
//组件中使用 $bus 存值
this.$bus.$emit('自定义事件类型名',传入的参数)
// 组件中使用 $on 取值
this.$bus.$on('自定义事件类型名',(data)=>{
//data 就是传入的参数
})
五. vue 插槽- slot
- 子组件预留位置(插槽),等待将来父组件可以写入自己代码 替代插槽
1. 匿名插槽
#父组件
<子组件>
#写入替换 slot插槽的html代码
</子组件>
#子组件
</div>
#子组件提前给父组件预留的位置 插槽
<slot></slot>
</div>
2. 具名插槽
- 如果同一个子组件中 出现了 两个或两个以上的插槽,必须使用具名插槽
- 父组件中的 标签属性 slot 找到子组件中 slot name相同的 进行一 一对应替换
#父组件
<子组件>
<div slot="插槽名1">
</div>
<div slot="插槽名2">
</div>
</子组件>
#子组件
<div>
<slot name="插槽名1"></slot> 、
<slot name="插槽名2"> </slot>
<div>
3. 作用域插槽
- 子组件自己有数据需要渲染到 slot 里面去 ,必须使用作用域插槽
- 作用域插槽必须是 基于具名插槽进行操作
#父组件
<子组件>
<div slot="插槽名1" slot-socpe="scope">
{{scope.属性名}}
</div>
<div slot="插槽名2">
</div>
</子组件>
#子组件
<div>
<slot name="插槽名1" :属性名="数据"></slot> 、
<slot name="插槽名2"> </slot>
<div>
4. v-slot指令
- v-slot 插槽指令,vue2.6.0 版本更新的一个指令,主要是 用来替代之前的 插槽的语法
- 匿名插槽
#父组件
<子组件>
<template v-slot>
#写入替换 slot插槽的html代码、
</template>
</子组件>
- 具名插槽 v-slot:插槽名
<template v-slot:title>
<div > 女装 </div>
</template>
#简化语法 v-slot: ===> #
<template #msg>
<div > 查看 <i class="iconfont icon-wode"></i></div>
</template>
- 作用域插槽
<template v-slot:title="scope" >
儿童服装{{scope.shopName}}
</template>
<template #title="scope" >
儿童服装1{{scope.shopName}}
</template>