尚硅谷视频:https://www.bilibili.com/video/BV1Zy4y1K7SH/?spm_id_from=333.999.0.0&vd_source=cec13bbe100bddfff8baf29d18ed8994
文章目录
- 模板语法
- data与el的2种写法
- MVVM模型
- 事件
- 事件修饰符
- 事件的基本使用
- 计算属性
- 简写形式
- 监视属性
- 绑定样式
- 条件渲染
- 列表渲染
- 收集表单数据
- 生命周期函数
- 非单文件组件
- 单文件组件
- Vue脚手架
- main.js
- App.vue
- 其他组件
- ToDoList总结
- vue组件什么是会被销毁
- Vuex
- 创建store
- 使用
模板语法
- 插值语法
<h1>
{{name}}
</h1>
- 指令语法
单向数据绑定
<h1 v-bind:href = "js表达式"></h1>
<h1 :href = "js表达式"></h1>
双向数据绑定 一般用在表单类元素上 默认收集value值
<input type = "text" v-model:value="name"/>
<input type = "text" v-model="name"/>
data与el的2种写法
-
el有2种写法
- (1).new Vue时候配置el属性。
- (2).先创建Vue实例,随后再通过vm.$mount(‘#root’)指定el的值。
-
data有2种写法
-
(1).对象式
data: { name: 'shanghai' }
-
(2).函数式
data (){ console.log('@@@', this); //此时this是Vue return { name: 'shanghai' } }
-
如何选择:目前哪种写法都可以,以后学习到组件时,data必须使用函数式,否则会报错。
- 一个重要的原则:
由Vue管理的函数(例如data),一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
MVVM模型
-
MVVM模型
- M:模型(Model) :data中的数据
- V:视图(View) :模板代码
- VM:视图模型(ViewModel):Vue实例
-
观察发现:
- data中所有的属性,最后都出现在了vm身上。
- vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
事件
<h1 @click="showInfo" >点我提示信息</h1>
事件修饰符
1.prevent:阻止默认事件(常用);
<a href="https://www.baidu.com" @click.prevent="showInfo" >点我提示信息</a>
2.stop:阻止事件冒泡(常用);
修饰符也可以连用,例如下面事例是即阻止冒泡同时也阻止默认行为
<div class="demo1" @click="showMsg(1)">
<button @click.stop="showMsg(2)">点我提示信息</button>
<!--修饰符也可以连用,例如下面事例是即阻止冒泡同时也阻止默认行为-->
<!--<a href="https://www.google.com.tw" @click.prevent.stop="showInfo">谷歌台湾</a>-->
</div>
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;
<!-- capture事件的捕获模式 让事件以捕获的方式来处理(先捕获再冒泡)-->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">div2</div>
</div>
5.self:只有event.target是当前操作的元素时才触发事件;
<!-- self 只有event.target是当前操作的元素时才触发事件(变相阻止冒泡)-->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
<!--passive:事件的默认行为立即执行,无需等待事件回调执行完毕;-->
<!--scroll滚动条一滚动就会触发的事件 wheel鼠标滚轮事件-->
<ul class="list" @scroll.passive="demo">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
正常输出2、1,@click.stop输出2, @click.capture输出1,2(先捕获再冒泡
事件的基本使用
- 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数!否则this就不是vm了
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象
- @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参
<button v-on:click="showInfo1">点我提示信息1</button>
<!--简写形式 @click-->
<button @click="showInfo2($event,66)">点我提示信息2</button>
showInfo2(e,num){
console.log(e.target);//
console.log(`接收参数:${num}`);
}
计算属性
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
- get函数什么时候执行?
- 初次读取时会执行一次。
- 当依赖的数据发生改变时会被再次调用。
- set函数什么时候执行?
- 修改计算属性所依赖的普通属性(放在data里面的)
- 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
- 备注:
- 计算属性最终会出现在vm上,直接读取使用即可。
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<div>
姓:<input type="text" v-model="firstName"/>
<br/><br/>
名:<input type="text" v-model="lastName"/>
<br/><br/>
测试:<input type="text" v-model="test"/>
<br/><br/>
全名: <input type="text" v-model="fullName"/>
<br/><br/>
全名: <span>{{ fullName }}</span>
</div>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
//data里的都是属性
firstName: '张',
lastName: '三',
test:'test'
},
//计算属性--> 旧属性加工
computed: {
fullName: {
//get的作用,当读取fullName时get就会被调用,且返回值就做为fullName的值
//defineProperty
//注意vm._data是不存在计算属性的
//计算属性算完之后直接丢到了viewModel实例对象身上
/**
* get的调用时机
* 1.初次读取计算属性时
* 2.所依赖的数据(data中的属性)发生变化时,不改变的话直接读缓存
* 3.methods没有缓存,读几次就调用几次无论要用的数据是否发生变化
* @returns {string}
*/
get(){
//此时get函数中的this指向是vm
console.log('get调用了', this);
return this.firstName + '-' + this.lastName
},
/**
* set什么时候调用
* 1.当fullName被修改时
* @param value
*/
set(value){
console.log('set调用了', value);
//修改计算属性所依赖的普通属性(放在data里面的)
//this === vm
const [ firstName, lastName ] = value.split('-');
console.log('firstName', firstName);
console.log('lastName', lastName);
this.firstName = firstName;
this.lastName = lastName; //依赖属性发生改变之后,计算属性自动改变
}
}
}
});
</script>
简写形式
computed: {
//简写形式
//前提:计算属性只考虑读取不考虑修改 set丢了
//简写计算书写为一个函数(这个函数当成getter使用), 注意不要写箭头函数
//执行完getter之后,vm直接保存返回的数据为fullName属性的属性值,此时vm.fullName不是函数而是一个确切的计算值
fullName: function (){
return this.firstName + '--' + this.lastName;
}
}
监视属性
-
当被监视的属性变化时, 回调函数自动调用, 进行相关操作
-
监视的属性必须存在,才能进行监视
-
监视的两种写法:
- new Vue时传入watch配置
- 通过vm.$watch监视
-
handler函数
- 当被监视属性发生改变就会调用该函数
- 接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
-
深度监视:
- Vue中的watch默认不监测对象内部值的改变(一层)
- 配置deep:true可以监测对象内部值改变(多层)
-
简写形式,不需要immediate、deep属性
watch:{ isHot(newValue, preValue){ console.log(newValue,preValue); } }
案例:
<div id="root">
<h1>今天天气很 {{ info }}</h1>
<button @click="changeWeather">
切换
</button>
<h3>b的值是:{{ numbers.b }}</h3>
<button @click="numbers.b++">
点我让b加一
</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
isHot: true,
numbers:{
a:1,
b:1,
d:{
e:2
}
}
},
//计算属性
computed: {
info(){
return this.isHot ? '炎热' : '凉爽';
}
},
//改变模版数据的方法
methods:{
changeWeather(){
this.isHot = !this.isHot;
}
},
// watch:{
// //监视的配置对象
// //watch不仅能监视data的普通属性,也可以检测计算属性
// isHot:{
// immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
// //handler 什么时候调用呢
// //当isHot发生改变就会调用该函数
// //handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
// handler(newValue, preValue){
// console.log('ishot 被修改了');
// console.log(`newValue: ${newValue}, preValue: ${preValue}`);
// }
// },
//深度监视numbers中的所有属性
numbers:{
deep: true, //监视多级结构的属性
handler(){
console.log('numbers 发生改变')
}
}
// }
});
//watch的第二种写法,直接采用vm对象实例
vm.$watch('isHot', {
immediate: true, //当这个属性为true时,页面刚渲染就运行handler函数
//handler 什么时候调用呢
//当isHot发生改变就会调用该函数
//handler接收两个参数,一个是这个状态参数改变前的值,另一个是改变后的旧值
handler(newValue, preValue){
console.log('ishot 被修改了');
console.log(`newValue: ${newValue}, preValue: ${preValue}`);
}
});
</script>
绑定样式
- class样式
- 写法:class=“xxx” xxx可以是字符串、对象、数组
- 字符串写法适用于:类名不确定,要动态获取。
- 对象写法适用于:要绑定多个样式,个数不确定,名字也不确定
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
- style样式
- :style="{fontSize: xxx}"其中xxx是动态值。
- :style="[a,b]"其中a、b是样式对象。
条件渲染
- v-if
- 写法:
(1).v-if=“表达式”
(2).v-else-if=“表达式”
(3).v-else=“表达式” - 适用于:切换频率较低的场景。
- 特点:不展示的DOM元素直接被移除。
- 注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。
- 写法:
- v-show
- 写法:v-show=“表达式”
- 适用于:切换频率较高的场景
- 特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
- 备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。
列表渲染
语法:
- 遍历数组、指定次数
v-for=“(item, index) in xxx” :key=“yyy”
-
遍历对象、字符串
v-for=“(val, key) of xxx” :key=“yyy”
v-for=“(c, index) of xxx” :key=“yyy”
-
在Vue修改数组中的某个元素一定要用如下方法,直接修改的话Vue不监听,模板不改变
- 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或 vm.$set()
- 原理:
Vue会监视data中所有层次的数据- 对象中后追加的属性,Vue默认不做响应式处理
- 利用vue.set(或者vm. s e t ( ) ) a p i 能够添加的属性变为响应式属性 V u e . s e t ( t a r g e t , p r o p e r t y N a m e / i n d e x , v a l u e ) 或 v m . set())api能够添加的属性变为响应式属性Vue.set(target,propertyName/index,value) 或 vm. set())api能够添加的属性变为响应式属性Vue.set(target,propertyName/index,value)或vm.set(target,propertyName/index,value)
- 特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!!!
methods:{
updateM(){
// this.persons[1].name = '马老师'; //奏效
// this.persons[1].age = 50; //奏效
// this.persons[1].sex = '男'; //奏效
// this.persons[1] = { id: '002', name: '马老师', age: 50, sex:'男' }; //这样修改vue是无法监测数据的
this.persons.splice(1,1,{ id: '002', name: '马老师', age: 50, sex:'男' });
},
addSex(){
//这里this === vm
//利用vue.set(或者vm.$set())api能够添加的属性变为响应式属性
//注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
Vue.set(this.stu, 'sex', '男')
// this.$set(this.stu, 'sex', '男');
}
}
“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。
Vue数据响应式与双向数据绑定原理区分
数据响应式是指通过数据驱动DOM视图的变化,是单向的过程,而双向数据绑定的数据和DOM是一个双向的关系https://blog.csdn.net/forever__fish/article/details/127163227
收集表单数据
- 若:,则v-model收集的是value值,用户输入的就是value值。
- 若:,则v-model收集的是value值,且要给标签配置value值。
- 若:
- 没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- 配置input的value属性:
- v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
- v-model的初始值是数组,那么收集的的就是value组成的数组
- 备注:v-model的三个修饰符:
- lazy:失去焦点再收集数据
- number:输入字符串转为有效的数字
- trim:输入首尾空格过滤
<div id="root">
<form @submit.prevent="demo">
<!--写了label则点击它也能使指定的input获取焦点 for属性的值为指定元素的id-->
<label for="demo">账号:</label>
<!--v-model主要用来双向绑定输入类表单value值-->
<input type="text" id="demo" v-model.trim="userInfo.account"/>
<br/>
密码: <input type="password" v-model="userInfo.password"/>
<br/>
性别:
<!--一组radio单选框的name值一定要相同 设置value值好让v-model去双向绑定-->
男:<input type="radio" v-model="userInfo.sex" name="sex" value="male"/>
女:<input type="radio" v-model="userInfo.sex" name="sex" value="female"/>
<br/>
年龄: <input type="number" v-model.number="userInfo.age"/>
<br/>
爱好:
<!--如果没有value值则v-model收集checked元素-->
学习 <input v-model="userInfo.hobby" type="checkbox" value="study"/>
打游戏 <input v-model="userInfo.hobby" type="checkbox" value="game"/>
吃饭 <input v-model="userInfo.hobby" type="checkbox" value="eat" />
<br/>
所属校区
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="Beijing">北京</option>
<option value="Shanghai">上海</option>
<option value="Shenzhen">深圳</option>
<option value="Wuhan">武汉</option>
</select>
<br/>
其他信息<textarea v-model.lazy="userInfo.other"></textarea>
<br/>
<input type="checkbox" v-model="userInfo.ifAgree"/>阅读并接受<a href="https://www.google.com.tw">《用户协议》</a>
<button>提交数据</button>
</form>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data:{
userInfo:{
account: '',
password: '',
sex: 'male',
age:'',
hobby:[],
city:'',
other:'',
ifAgree:''
}
},
methods:{
demo(){
//json转换为string
console.log(JSON.stringify(this.userInfo));
}
}
})
</script>
生命周期函数
<script>
export default {
name: "demo",
data() {
return {
msg: "Welcome to Your Vue.js App",
msg1: "Welcome to Your Vue.js App1",
}
},
methods: {
reverseMessage() {
this.msg = this.msg.split("").reverse().join("")
}
},
computed: {
fullMessage: function() {
return this.msg + " " + this.msg
},
fullMessage2: {
get() {
console.log("get 调用了")
},
set(value) {
console.log("set 调用了");
this.msg = value.split(" ").slice(0, 1).join("");
}
}
},
watch: {
msg(val, oldVal) {
console.log("watch msg 调用了", val, oldVal)
},
msg1:{
immediate:true,
deep:true,
handler(val,oldVal){
console.log("watch 调用了 msg1", val, oldVal)
}
}
}
}
</script>
非单文件组件
-
Vue.extend() 是vc对象
-
new vue()是vm对象
-
const school = Vue.extend(options) 可简写为:const school = options
-
为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。所以vm与vc属性配置并不是一模一样,尽管vc底层复用了很多vm的逻辑
-
一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
-
为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。
<script type="text/javascript">
Vue.config.productionTip = false;
const school = Vue.extend({
template: `
<div>
<h1>学校名称:{{ name }}</h1>
<h1>学校地址:{{ address }}</h1>
<button @click="showname">点我提示学校名</button>
</div>
`,
data(){
return {
name: '武汉科技大学', //vuecomponent的实例对象
address:'武汉'
}
},
methods:{
showname(){
console.log(this);
console.log(this.name);
}
}
});
// console.log(typeof school, school); //所谓的组件就是构造函数(VueComponent);
//测试组件
const test = Vue.extend({
template: `<h1>panyue</h1>`
});
//hello组件
const hello = Vue.extend({
template:`
<div>
<h1>{{ msg }}</h1>
<test></test>
</div>`,
data(){
return {
msg: '你好'
}
},
components: {
test
}
})
const vm = new Vue({
el:"#root",
components:{
school,
hello
}
});
//验证school与hello并不是同一个VueComponent构造函数
// school.a = 99;
// console.log('@', school);
// console.log('#', hello);
// console.log(school === hello);
// console.log(school.a, hello.a);
</script>
单文件组件
-
普通组件School.veu
- template 写HTML
- script 创建vc 暴露vc export default {}
- style 写css
-
App组件 汇总其他组件 components:{School, Student}
-
main.js 创建vm
//创建vm import App from './App'; //如果文件 new Vue({ el: '#root', template:`<App></App>`, components:{ App } });
-
index.html 准备root容器
mixin
Vue脚手架
- npm install -g @vue/cli
- vue create xxx
- 启动项目 npm run serve
- 打包项目 npm run build
main.js
入口文件
import {createApp} from 'vue'
import App from './App.vue'
import * as VueRouter from 'vue-router';
import routes from "./config/route";
import Vant from 'vant';
import 'vant/lib/index.css';
import '../global.css'
const app = createApp(App);
const router = VueRouter.createRouter({
// 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: VueRouter.createWebHistory(),
routes, // `routes: routes` 的缩写
})
app.use(Vant);
app.use(router);
app.mount('#app')
App.vue
//vue2
<template>
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
}
}
</script>
//vue3
<template>
<BasicLayout />
</template>
<script setup lang="ts">
import BasicLayout from "./layouts/BasicLayout.vue";
</script>
<template>
<BasicLayout />
</template>
<style>
</style>
其他组件
<template>
<van-nav-bar
:title="title"
left-arrow
@click-left="onClickLeft"
@click-right="onClickRight"
>
<template #right>
<van-icon name="search" size="18"/>
</template>
</van-nav-bar>
<div id="content">
<router-view/>
</div>
<van-tabbar route>
<van-tabbar-item to="/" icon="home-o" name="index">主页</van-tabbar-item>
<van-tabbar-item to="/team" icon="search" name="team">队伍</van-tabbar-item>
<van-tabbar-item to="/user" icon="friends-o" name="user">个人</van-tabbar-item>
</van-tabbar>
</template>
<script setup lang="ts">
import { useRouter } from "vue-router";
import {ref} from "vue";
import routes from "../config/route";
const router = useRouter();
const DEFAULT_TITLE = '伙伴匹配';
const title = ref(DEFAULT_TITLE);
/**
* 根据路由切换标题
*/
router.beforeEach((to, from) => {
const toPath = to.path;
const route = routes.find((route) => {
return toPath == route.path;
})
title.value = route?.title ?? DEFAULT_TITLE;
})
const onClickLeft = () => {
router.back();
};
const onClickRight = () => {
router.push('/search')
};
</script>
<style scoped>
#content {
padding-bottom: 50px;
}
</style>
ToDoList总结
vue组件什么是会被销毁
页面关闭、路由跳转、v-if和改变key值
Vuex
创建store
./src/store/index.js
import {createStore} from "vuex";
import {nanoid} from "nanoid";
const state = {
todos: JSON.parse(localStorage.getItem('todos')) || []
};
const mutations = {
saveTodos(state, value) {
localStorage.setItem('todos', JSON.stringify(value))
},
addTodo(state, input) {
state.todos.unshift({
id: nanoid(),
label: input,
done: false
})
},
allChecked(state, checked){
for(let i=0;i<state.todos.length;i++){
state.todos.splice(i,1,{...state.todos[i], done: checked})
}
},
delDone(state){
console.log("delDone:", state.todos)
const todoss = state.todos.filter(item => !item.done);
// 有两个值时,第一个值为删除的位置,第二个值为删除的个数;
state.todos.splice(0,state.todos.length)
// 有三个或者多个值时,第一个值为插入元素的位置,第二个值为替换的个数,后面的值都为插入的新元素;
state.todos.splice(0,todoss.length,...todoss)
},
editTodo(state, [id, label]){
const index = state.todos.findIndex(item => item.id === id);
state.todos[index].label = label;
}
};
const getters = {
done(state) {
return state.todos.filter(item => item.done).length;
},
total(state) {
return state.todos.length;
}
};
// 创建一个新的 store 实例
const store = createStore({
state,
mutations,
getters
});
export default store;
使用
main.js
app.use(store);
./src/App.vue
<template>
<Layout/>
</template>
<script setup>
import { reactive, provide ,watch, getCurrentInstance} from 'vue'
import Layout from "@/components/Layout.vue";
import {nanoid} from 'nanoid';
import { useStore } from 'vuex'
const proxy = getCurrentInstance();
console.log(proxy);
// const readStore = proxy.$store.state.todos;
const store = useStore()
const readStore = store.state.todos;
const todos = reactive(readStore)
watch(todos, (todos, prevTodos) => {
store.commit('saveTodos', todos)
},{ deep: true })
</script>
<style>
</style>
./src/components/MyFooter.vue
<script setup>
import {ref, inject, getCurrentInstance, watch, computed} from 'vue'
import {useStore, mapGetters} from "vuex";
const store = useStore();
const todos = store.state.todos
// 使用computed保留响应式
const done = computed(()=>{
return store.getters.done
});
const total = computed(()=>{
return store.getters.total
})
// const delDone = inject('delDone');
const checked = ref(false)
// const done = ref(todos.filter(item => item.done).length)
// const total = ref(todos.length)
//
// watch(todos,(todos, prevTodos) => {
// console.log("todos Change", todos);
// console.log("done", done);
// console.log("total", total);
//
// done.value = todos.filter(item => item.done).length;
// total.value = todos.length;
//
// },
// { deep: true }
// )
function allChecked() {
store.commit("allChecked",checked.value);
// checked.value =!checked.value;
}
function delDone(){
store.commit("delDone");
checked.value = false;
}
</script>
<template>
<el-row :gutter="5">
<el-col :span="10" >
<el-checkbox label="全部完成" @change="allChecked()" v-model="checked"/>
</el-col>
<el-col class="mx-0" :span="10" >
<el-text class="mx-1" size="default">完成情况:{{done}}/{{total}}</el-text>
</el-col>
<el-col :span="4">
<el-button type="danger" size="small" @click="delDone">Del Done</el-button>
</el-col>
</el-row>
</template>
<style scoped>
.el-row {
height: 100%;
padding: 2px 2px;
}
.el-button{
font-weight: normal;
margin: 0 auto;
width: 100%;
height: 100%;
text-align: center;
}
.mx-0 {
text-align: center;
}
.mx-1 {
height: 100%;
width: 100%;
margin: 0 auto;
}
</style>
使用computed保留getters响应式
// 使用computed保留响应式
const done = computed(()=>{
return store.getters.done
});
const total = computed(()=>{
return store.getters.total
})