本Wiki将会介绍Vue的基础语法, 您在按照本教程学习之前建议掌握以下基础:
- HTML基础。 了解什么是HTML并会使用编译器编写html文件, 了解html中的基本标签
- CSS基础。 明白CSS的作用和语法并会简单的使用
- JavaScript基础。需要掌握基础关键字, 数据类型;知道DOM, BOM, 了解面向过程编程和面向对象编程的区别。还要了解异步JavaScript 和一些网络编程基础
- 最好但是不强求知道如何使用node.js 以及 npm包管理工具的使用
这部分内容会从vue指令讲起,到组件通信结束
v-bind
作用 : 动态设置html元素的标签属性 → src url title …
//语法:
v-bind:属性名="表达式"
//示例:
<div id="app">
<img v-bind:src="imgUrl" v-bind:title="msg">
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "app",
data: {
imgUrl: 'img',
msg: '小小kitten qvq'
}
})
</script>
v-for
作用 : 基于数据循环 , 多次渲染整个元素 → 数组 , 对象 , 数字。遍历数组示例 :
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id">
{{ item.name }} → {{ item.id }}
<button @click="del(item.id)">delete</button>
</li>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "app",
data: {
list:[
{id:1,name:'西瓜'},
{id:2,name:'苹果'},
{id:3,name:'芭娜娜'}
]
},
methods:{
del(id){ //拿到数组中的每一项 → item
this.list = this.list.filter(item => item.id !== id)
}
}
})
</script>
v-for 中的 key
v-for 的默认行为会尝试原地修改元素
:key的作用是给每一项添加一个唯一标识(目的是为了高效地更新虚拟DOM) , 这里给第一个元素添加一个背景色 , 然后点击删除按钮 , 如果没有唯一标识 , 那么vue就不能正确识别 v-for 从数组中渲染的结构 , 此时新的第一个 li 标签仍然会有背景色
尽量不要使用索引值index作key值,一定要用唯一标识的值,如id等。因为若用数组索引index为key,当向数组中指定位置插入一个新元素后,因为这时候会重新更新index索引,对应着后面的虚拟DOM的key值全部更新了,这个时候还是会做不必要的更新
v-model
作用 : 给表单元素使用 , 双向绑定数据 → 可以快速 获取或设置 表单内容元素
本质上是一个语法糖。就是 value属性 和 input事件 的和写
<div id="app">
<input :value="msg" @input="msg = $event.target.value" type="text">
</div>
示例 (待办) :
<div id="app">
<input type="text" placeholder="请输入今天要做的事" class="entering" v-model="todoName">
<button class="add" @keyup.enter="add" @click="add">添加</button>
<section class="sum" >合计:{{ list.length }}<span class="destroy" @click="clear">清空</span></section>
<section class="main">
<ul class="todoList">
<li class="todo" v-for="(item,index) in list" :key="item.id">
<div class="view">
<!-- 建议使用index 而不用item.id是因为删除时序号的顺序会动态刷新 -->
<span class="index">{{ index + 1 }}.</span><label >{{ item.taskName }}</label>
<button class="del" @click="del(item.id)">删除</button>
</div>
</li>
</ul>
</section>
</div>
<script src="./lib/vue.js"></script>
<script>
//v-model 绑定输入框,实时获取表单元素内容
const app = new Vue({
el:"#app",
data:{
list:[
{id:1,taskName:"跑步十公里"},
{id:2,taskName:"跳绳一百下"},
{id:3,taskName:"买宝宝爽身粉"}
],
todoName:'',
},
methods:{
del(id){
// this.list = this.list.filter(function(item){
// return item.id !== id
// })
this.list = this.list.filter(item => item.id !==id)
},
add(){
if(this.todoName.trim() === ''){
alert('Please input your task name')
return
}
//unshift()方法:将元素添加到数组的开头,并返回数组的长度
this.list.unshift({
id: +new Date(),
taskName: this.todoName,
})
//清空输入内容
this.todoName = ''
},
clear(){
if(confirm("确定清空?")){
this.list = []
}
}
},
})
</script>
指令的修饰符
- 按键修饰符 @keyup.enter → 键盘回车监听
- v-model修饰符 v-model.trim → 去除首尾空格 v-model.number → 转数字
- 事件修饰符 @事件名.stop → 阻止冒泡 @事件名.prevent → 阻止默认行为
修饰符就是对指令进行了一些封装 , 拿到事件对象 e , 这里以@keyup.enter为例 , 会监听键盘事件 , 如果 e.key === 'Enter' , 那么就触发事件
计算属性computed
概念 : 基于现有的数据 , 计算出来的新属性 。依赖的数据变化,自动重新计算 。
声明在 computed 配置项中 , 一个计算属性对应一个函数 , 函数里是计算逻辑
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="item.id">
{{ item.name }} → {{ item.id }} → {{ item.number }}
<button @click="del(item.id)">delete</button>
</li>
<span>总共:{{ totalnumber }}个</span>
</ul>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "app",
data: {
list:[
{id:1,name:'西瓜',number:4},
{id:2,name:'苹果',number:7},
{id:3,name:'芭娜娜',number:9}
]
},
methods:{
del(id){ //拿到数组中的每一项 → item
this.list = this.list.filter(item => item.id !== id)
}
},
computed: {
totalnumber() {
return this.list.reduce((sum, item) => sum + item.number, 0)
}
}
})
</script>
①computed 与 ②methods
- ①作用 : 封装了一段对于数据的处理 , 求得一个结果 使用 : 写在 computed 配置项中 , 作为属性 , 直接使用 → this.计算属性 / {{ 计算属性 }}
- ②作用 : 给实例提供一个方法 , 调用以处理业务逻辑
computed计算属性具有缓存特性 会对计算出来的结果缓存 , 再次使用直接读取缓存 , 依赖项变化了 , 会自动重新计算→并再次缓存
计算属性的完整写法
//简单写法只配置了获取,我们无法修改计算属性的值,比如在methods里添加修改计算属性的方法就会报错,下面是完整写法
<script>
const app = new Vue({
el: "app",
data: {},
computed:{
计算属性名:{
get(){
//计算逻辑
return 结果
},
set(修改的值){
//修改逻辑
}
}
}
})
</script>
//示例
<div id="app">
姓:<input type="text" v-model="firstname"> + 名:<input type="text" v-model="lastname"> = <span>{{ fullname }}</span>
<button @click="changeName">改名卡</button>
</div>
<script src="./lib/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
firstname:'刘',
lastname:'备'
},
methods:{
changeName(){
//调用changeName()方法时,会修改fullname这个计算属性,那么就会调用其set()方法
this.fullname = '吕小布'
}
},
computed:{
fullname :{
get(){
return this.firstname+this.lastname
},
set(value){
this.firstname = value.slice(0,1)
this.lastname = value.slice(1)
}
}
}
})
</script>
watch侦听器
作用 : 监视数据变化 , 执行一些 业务逻辑 或 异步操作
<div id="app">
<input type="text" v-model="words"/>
<input type="text" v-model="obj.name"/>
</div>
<script src="./vue.js"></script>
<script>
const app = new Vue({
el: "app",
data: {
words:'小小kitten qvq',
obj:{
id:1,
name:buddy
}
},
watch: {
//该方法会在数据变化时,触发执行
数据属性名(newVal,oldVal){
//一些业务逻辑 或 异步操作
}
'对象.属性名' (newVal,oldVal) {
//一些业务逻辑 或 异步操作
}
}
})
</script>
Vue生命周期 和 生命周期的四个阶段
vue生命周期 : 一个Vue实例从创建到销毁的整个过程
生命周期四个阶段 : 创建 挂载 更新 销毁
- 创建响应式数据
- 渲染模板 → 操作dom
- 数据修改,更新视图
- 销毁实例
Vue生命周期 过程中 , 会自动运行一些函数 , 被称为 生命周期钩子
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate → 数据修改了 , 视图还没更新
- updated
- beforeDestory
- destoryed
//示例: mounted应用 获取焦点
<div class="container" id="app">
<div class="search-container">
<div class="search-box">
<input type="text" v-model="words" id="inp">
<button>搜索一下</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
words: ''
},
mounted() {
document.querySelector('#inp').focus()
}
})
</script>
工程化开发 & vue-cli(可以前置学习webpack)
传统开发模式是直接引入核心包 , 开发vue
工程化是基于构建工具 webpack 等开发 , 但是存在问题
- webpack的配置不简单
- 雷同的基础配置
- 缺乏统一标准
所以就需要一个统一的架子 , 可以快速开发Vue 项目 , 好处是
- 容易配置
- 内置babel等工具 → 打包处理高级请求
- 标准化
使用步骤:
全局安装: yarn global add @vue/cli || npm i @vue/cli -g (node环境下npm指令在所有目录中生效)
查看vue版本: vue --version
启动项目架子创建项目: vue create project-name 项目名不能用中文
启动项目 yarn serve || npm run serve (node环境下)
创建项目后,可以看到有很多文件,这里挑几个重点的说,其它自行了解
main.js
/** main.js **/
import Vue from 'vue'
import App from './App.vue'
//导入 核心包 和 App.vue根组件
//当前处于什么环境 false生产环境 true开发环境(具体区别可以看webpack)
Vue.config.productionTip = false
//vue实例化 render方法→基于App.vue创建结构渲染 index.html → 将App.vue渲染到index.html中
new Vue({
// el:'#app', 和下面的$mount 作用一致,用于指定Vue所管理容器
render: h => h(App), //这里是简写
// render:(createElement) =>{
// return createElement(App)
// }
}).$mount('#app')
App.vue
<template> → 结构
<div @click="fn">
hello
</div>
</template>
<script> → 行为
export default{
methods:{
fn(){
alert("hello")
}
},
created(){
console.log("hello")
}
}
</script>
<style> →样式
div{
width:200px;
height:200px;
border: 3px solid #4576f4;
}
</style>
--------------------------------------
<!-- vue2中,template标签里 只能有一个根元素!
<style lang="less"> 让组件支持less需要装包 → npm i less less-loader
vscode中可以装插件(vetur,或volar)使得能够自动识别vue文件(生产提示,字体颜色等等)
-->
组件化开发
组件化:一个页面可以拆分成一个个组件 , 每个组件都有自己独立的结构 , 样式 , 行为
好处 : 便于维护, 利于复用 → 提升开发效率
局部组件的使用
1.创建组件 2.导入 3.使用
- 局部注册 : 只能在注册的组件内使用
- 全局注册 : 所有组件内都能使用
导入 : 在App.vue中import对应路径
使用 : 在App.vue中 使用
// 先在commponents 文件夹中创建组件headNav.vue
<template>
<div class="head">
The Summer Kitten
</div>
</template>
<script>
export default{}
</script>
<style>
.head{
width: 600px;
height: 100px;
background-color: blueviolet;
color: whitesmoke;
text-align: center;
font-size: 28px;
line-height: 100px;
}
</style>
// 再在App.vue中 导入和使用
<template>
<div class="app">
<Header></Header>
</div>
</template>
<script>
//导入 , 得到对象
import Header from './components/headNav.vue';
export default{
components:{
//组件名 : 组件对象
Header : Header
}
}
</script>
<style></style>
//自行添加另外两个组件: bodyMain 和 footNav
全局组件的使用
示例: 全局注册一个组件greenButton
先在components目录下创建一个组件greenButton.vue,写好样式 , 然后在main.js里全局注册
/* main.js 文件 */
//这一段尽量写在 调整开发环境的上面 , 导入为先
//全局注册
import greenButton from "./components/greenButton.vue"
// 注册名 , 注册对象
Vue.component('greenButton',greenButton)
组件的样式冲突scoped
默认情况 : 写在组件中的样式会全局生效 → 容易造成多个组件之间的样式冲突问题
给组件中的<style>加上scoped属性 , 可以让样式局部生效
<style scoped>
div{
border: 3px solid blue;
margin: 30px;
}
/* 本样式只会对当前组件生效 , 不会影响页面中所有的div */
<style>
<!--原理 : 本组件中的所有元素都会默认增加一个 data-v-hash 自定义属性,再将选择器更改
可在浏览器的开发者工具中看到:
div[data-v-...]{
//样式
}
这里的...是一个随机值 -->
组件通信
data是一个函数
一个组件的data选项必须是一个函数 → 保证每个组件实例 , 维护独立的一份数据对象
每次创建新的组件实例 , 都会新执行一次data函数 , 得到一个新对象
示例 :
/* App.vue */
<template>
<div class="app">
<baseCount></baseCount>
<baseCount></baseCount>
<baseCount></baseCount>
</div>
</template>
<script>
import baseCount from './components/BaseCount'
export default {
components: {
baseCount,
},
}
</script>
<style></style>
/* baseCount.vue */
<template>
<div class="base-count">
<button @click="count--">-</button>
<span>{{ count }}</span>
<button @click="count++">+</button>
</div>
</template>
<script>
export default {
// data() {
// console.log('函数执行了')
// return {
// count: 100,
// }
// },
data: function () {
return {
count: 100,
}
},
}
</script>
<style>
.base-count {
margin: 20px;
}
</style>
上述示例在App.vue中使用了basecount组件3次 , 三个组件中 每一个 都有一个data函数,函数中维护了 一个count 数据 , 但是这三个 组件维护的不是 同一个count , 而是分别属于他们自己的count , 因此在修改时 , 不会出现点击一个 +按钮 , 页面上三个数据同步修改的情况 , 这就体现了data 作为一个函数的好处
组件通信概念
指组件与组件之间的数据传递 , 因为组件的数据是独立的 , 所以无法访问其他组件的数据,这时就需要组件通信
不同组件关系 和 组件通信方案分类(3种)
组件关系 : 父子 非父子
通信方案 : props 和 $emit provide & inject || eventbus
通用解决方案 : vuex
1.props和 $emit
父组件通过props的方式向子组件传递数据,而通过$emit 子组件可以向父组件通信。
2.event bus 事件总线
作用 : 非父子组件之间 , 进行简单消息传递
- 创建 一个事件总线 (空 vue 实例) → utils/EventBus.js
- 监听方监听事件
- 发送方触发Bus实例 的事件
3.provide & inject
- 父组件provide提供数据
- 子/孙 inject 取值使用
示例 :
src目录下新建 utils 文件夹,里面新建EventBus.js文件
//1.创建一个都能访问的 事件总线
//得到Vue这个实例
import Vue from 'vue'
//
const Bus = new Vue()
//把当前实例导出
export default Bus
/* App.vue */
<template>
<div id="app">
<sonFirst :a="title[index]" :b="indexobj" @changeTitle="handleChange" @changeBorder="handleChangeBorder"></sonFirst>
<mainBody :b="index"></mainBody>
</div>
</template>
<script>
import sonFirst from './components/sonFirst.vue';
import mainBody from './components/mainBody.vue';
export default {
name: 'App',
//父组件提供数据 , 子/孙/... 组件渲染
provide(){
return {
index:this.index, //传递自己本身有的数据,有两种方式,一种是简单数据类型,一种是复杂数据类型
indexobj:this.indexobj
}
},
data() {
return {
index:0,
//演示provide和inject
indexobj:{
index:0
},
title:['Spring','Summer','Autumn','Winter'],
}
},
methods:{
handleChange(newTitleIndex){
this.index = newTitleIndex
},
handleChangeBorder(newBorderIndex){
this.indexobj.index = newBorderIndex
}
},
components: {
sonFirst: sonFirst,
mainBody: mainBody,
}
}
</script>
<style></style>
/* commponents 下面的 sonFirst.vue 文件 */
<template>
<div class="main">
<div
:class="{ spring: backgroundIndex[0], summer: backgroundIndex[1], autumn: backgroundIndex[2], winter: backgroundIndex[3] }">
The {{ a }} Kitten</div>
<button @click="change">更换季节</button>
</div>
</template>
<script>
import Bus from '../utils/EventBus.js'
export default {
data() {
return { index: 0, backgroundIndex: [true, false, false, false] ,lock:true}
},
props: ['a','b'],
methods: {
//1.通过 $emit(),向父组件发送消息通知
change() {
if (this.lock) {
this.index++
if (this.index > 3) { this.index = 0 } //这里必须放上面
//不然 index++后,backIndex数组会自动扩充
this.backgroundIndex.forEach((element, index) => {
this.backgroundIndex[index] = false
});
this.backgroundIndex[this.index] = true
this.$emit("changeTitle", this.index)
this.$emit("changeBorder",this.index) //演示改变复杂类型
this.lock = false
setTimeout(()=>{this.lock = true},1500)
}
},
},
created(){
//2.接收方 , 监听Bus的事件 , 监听后再回调函数
Bus.$on('sendMsg',(msg)=>{
alert(msg)
})
}
};
</script>
<style scoped>
div {
margin: 0 auto;
width: 900px;
height: 100px;
border-radius: 5px;
color: aliceblue;
font-size: 36px;
text-align: center;
line-height: 100px;
}
.spring {
background-color: #3afe80;
}
.summer {
background-color: #fe6a80;
}
.autumn {
background-color: #e2e23f
}
.winter {
background-color: blueviolet;
}
button {
position: absolute;
top: 10px;
left: 0;
margin: 0 auto;
background-color: rgb(106, 246, 141);
color: white;
width: 75px;
height: 30px;
border: 0;
font-size: 16px;
box-sizing: content-box;
border-radius: 5px;
}
</style>
/* mainBody.vue 文件 , 里面有一个子组件 grandSon */
<template>
<div class="main">
<button @click="clicksend">查看天气情况</button>
<grandSon></grandSon>
</div>
</template>
<script>
import Bus from '../utils/EventBus.js'
import grandSon from './grandSon.vue'
export default {
data(){
return{
alertText:["今天天气不错","今天有点热呢","今天很凉爽哇","今天有点冷哦"]
}
},
components:{
grandSon:grandSon
},
props:['b'],
methods:{
clicksend(){
// 触发事件
Bus.$emit('sendMsg',this.alertText[this.b])
},
}
}
</script>
<style scoped>
.main{
margin: 10px auto;
width: 900px;
height: 600px;
border-radius: 8px;
background-color: burlywood;
}
button {
display: block;
margin: 10px auto;
background-color: rgb(87, 175, 101);
color: white;
width: 100px;
height: 45px;
border: 0;
color: ghostwhite;
font-size: 16px;
box-sizing: content-box;
border-radius: 5px;
}
</style>
/* grandSon组件 */
<template>
<div >
<div class="sonMain" :style="{'border-color':borderColor[index]}"></div>
<div class="sonMain" :style="{'border-color':borderColor[indexobj.index]}"></div>
</div>
</template>
<script>
export default {
//简单类型是非响应式的,不会随着父/爷 数据的更新而更新,复杂类型是响应式的,会更新
inject:['index','indexobj'],
data(){
return{
borderColor:['darkgreen','rebeccapurple','greenyellow','blueviolet']
}
}
}
</script>
<style scoped>
.sonMain{
margin: 10px auto;
border: 3px dotted darkgreen;
width: 850px;
height: 120px;
text-align: center;
}
</style>
vue2的基础部分就到这里了,后面会介绍一些其它的东西

Comments NOTHING