官网
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
# latest stable
$ npm install vue
vue同微信小程序一样都是声明式编程,并不是命令式编程。
要想使用vue,先导入vue
<script src="../js/vue.js">script>
然后创建vue对象,创建对象时可以穿一些参数,el表示的是vue可以控制的标签及其字标签,data表示vue存储的数据
const app = new Vue({
el:"#app",
data:{
message: 'HelloWorld'
}
})
在vue控制的标签中可以展示data中的数据
<div id="app">
<div>{
{message}}div>
<div>
<h2>{
{message}}h2>
div>
div>
也可以修改data中的数据
app.mes sage="hello vue"
使用v-for="(数组每一个元素的名称,下标名) in 数组名"
<li v-for="(item,index) in movies">{
{
item}}</li>
完整代码
<div id="app">
<ul>
<li v-for="(item,index) in person">{
{item}}li>
ul>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el:"#app",
data:{
message:"helloWorld",
person:["海王","水鬼","绿茶","舔狗"]
}
})
script>
计数器 点击+按钮:数加一 点击-按钮:数减一
语法:绑定监听事件 v-on:事件名=“方法名”
vue的方法要写到vue的methods属性中
methods:{
add(){
console.log("add被执行了");
this.counter++;
},
sub(){
console.log("sub被执行了");
this.counter--;
}
}
完整代码
<div id="app">
<div>当前计数:{
{counter}}div>
<button v-on:click="add">+button>
<button @click="sub">-button>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el:"#app",
data:{
counter:0
},
methods:{
add(){
console.log("add被执行了");
this.counter++;
},
sub(){
console.log("sub被执行了");
this.counter--;
}
}
})
script>
生命周期:即事物从诞生到消亡的过程。
vue的生命周期就是vue从被创建到销毁的过程,在这个过程中有许多时间点,例如created(创建)、destroy(销毁)都会调用我们写的方法,在这些方法里我们就可以指定js在vue的某个时间点干某个事情。
const app = new Vue({
el:"#app",
data:{
counter:0
},
methods:{
add(){
console.log("add被执行了");
this.counter++;
},
sub(){
console.log("sub被执行了");
this.counter--;
}
},
created(){
console.log("created");
},
mounted(){
console.log("mounted");
}
<div id="app">
{
{message}}
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data:{
message: "你好呀"
}
})
script>
<div id="app">
<h2>{
{xing + ming}}h2>
<h2>{
{xing + " " + ming}}h2>
<h2>{
{xing}} {
{ming}}h2>
<h2>{
{counter * 2}}h2>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data:{
xing: "张",
ming: "三",
counter: 100
}
})
script>
<div id="app">
<div v-html="message">div>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data:{
message: "百度一下"
}
})
script>
<div v-text="message">div>
<div>{
{message}}div>
<div v-text="message">你好div>
<div v-pre >{
{message}}div>
<style>
[v-cloak] {
display: none;
}
style>
<div id="app">
<div v-cloak>{
{message}}div>
div>
<script src="../js/vue.js">script>
<script>
setTimeout(function (){
const app = new Vue({
el: "#app",
data:{
message: "你好"
}
})
},1000)
script>
在vue中属性绑定与小程序不同,不能直接使用mustache语法,需要使用v-bind
语法: <标签 v-bind:属性名=“表达式”>
语法糖:<标签 :属性名=“表达式”> //只写冒号
这样写表达式中返回的值就会自动绑定到该属性上
<img v-bind:src="src" alt=""/>
<img :src="src" alt=""/>
data:{
src:'https://img.alicdn.com/imgextra/i1/1735000042/O1CN01gGDauN1CBHJnirwrz_!!0-saturn_solar.jpg_468x468q75.jpg_.webp',
url:'https://www.bilibili.com'
}
{ {message}}
{ {message}}
对于style属性也可以绑定对象或者数组
有时候我们获取到的数据需要处理后才能在前端展示,对于简单的处理,例如加减我们可以在mustache中解决,对于复杂的处理写一个方法是更方便的。这就用到了计算属性
vue对象有一个computed属性,他的值都是方法,在mustache中写方法名即可获取到他的返回值。
<div id="app">
{
{fullName}}
总价:{
{allPrice}}
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
firstName: "张",
lastName: "三",
fruits: [
{
name: "桃子", price: 30},
{
name: "香蕉", price: 20},
{
name: "西瓜", price: 50},
{
name: "金苹果", price: 100},
]
},
computed: {
fullName(){
//拼接字符串
return this.firstName + this.lastName;
},
allPrice(){
//求和
return this.fruits.reduce((sum,item)=>{
return sum + item.price;
},0)
}
}
})
script>
以fullName为例,在vue的computed中下面两种代码是等价的,前者是后者的简写
fullName: function (){
return this.firstName + this.lastName;
},
fullName: {
get: function (){
return this.firstName + this.lastName;
}
}
也就是说fullName实质上是一个对象,当需要他的时候就会调用get方法返回值。
当然他也有set方法,不过很少能用到,他会在fullName值改变的时候被调用,并且如果没有set方法,fullName的值将不能被改变
fullName: {
set: function (value){
console.log(value);
},
get: function (){
return this.firstName + this.lastName;
}
}
看到这里你会发现,在methods里写一个方法专门去处理我们的数据然后返回,也能实现computed的效果。那他们两个有什么不同呢?
computed是有缓存的,当多次调用该数据时,methods会多次调用方法,而使用computed只会调用一次,极大地提高了效率。并且当方法中用到的数据发生改变时,vue会从新获取computed的值到缓存中。建议需要处理数据的使用就用computed
<div id="app">
{
{fullName}}
{
{fullName}}
{
{fullName}}
{
{fullName}}
{
{getFullName()}}
{
{getFullName()}}
{
{getFullName()}}
{
{getFullName()}}
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
firstName: "张",
lastName: "三",
},
methods: {
getFullName: function (){
const fullName = this.firstName + this.lastName;
console.log("getFullName():"+fullName)
return fullName;
}
},
computed: {
fullName: {
set: function (value){
console.log(value);
},
get: function (){
const fullName = this.firstName + this.lastName;
console.log("fullName:"+fullName)
return fullName;
}
}
}
})
script>
可以看到getFullName被调用了四次,而fullName的get方法被调用了一次
基础语法: 前者click是监听事件名,后者是要调用的函数名
语法糖: v-on: 等价于 @
如何传参: $event代表的是事件对象,普通变量从app.data中获取
//会报错
//传入事件对象,可以不接收
//传入指定变量
//传入事件对象和指定变量
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "你好呀",
name: "张三"
},
methods: {
click(){
console.log("按钮被点击了");
},
click2(name){
console.log(name);
},
click3(event,name){
console.log(name);
console.log(event);
}
}
})
</script>
语法:@事件名.修饰符=“调用方法”
<form action="www.baidu.com">
<input type="submit" value="按钮" @click.prevent="click1"/>
form>
//按回车上抬后才触发通过条件判断某个标签是否显示
语法:
<标签名 v-if=“布尔表达式”>
<标签名 v-else-if=“布尔表达式”>
<标签名 v-else>
<div v-if="score >= 90">优秀div>
<div v-else-if="score >= 80">良好div>
<div v-else-if="score >= 60">及格div>
<div v-else>不及格div>
vue在底层实现的时候会把需要显示的标签缓存一下,v-if为true的时候修改一下id、class等参数值后使用,但是在input标签的value并不会修改,这会导致input标签显示的时候value不为空。
解决办法:在我们不需要缓存的标签上添加key属性,标签的key不一样他们就不会互相使用了。
<span v-if="isUser">
<div>用户名div>
<input type="text" placeholder="请输入用户名" key="username" />
span>
<span v-else>
<div>邮箱div>
<input type="text" placeholder="请输入邮箱" key="email" />
span>
v-show与v-if功能相似,v-show的值为true的时候,该标签会被显示出来,反之不会。
不同点:v-show为false时,vue会给该标签style添加display:none ,而v-if为false时,html中就根本没有该标签
建议:标签内容频繁展示和隐藏时用v-show,标签展示切换不频繁用v-if
语法 <标签名 v-for="(元素变量,下标变量) in 数组名" >
<ul>
<li v-for="(item,index) in message" :key="item.name" >{
{index + ":" + item.name + "-" + item.age}}li>
ul>
message: [
{name: "张三", age: 11},
{name: "李四", age: 19},
{name: "王五", age: 13},
{name: "赵六", age: 14},
]
说明:item和index不是必须写的,可以只写一个,写一个的时候可以没有小括号
说明:建议添加一个属性 :key 来指定数组元素的唯一标识,这样在修改数组时更新数据会更快。
语法 <标签名 v-for="(值,变量名,下标) in 对象名" >
<ul>
<li v-for="(value,key,index) in person" >{
{index + ":" + key + "-" + value}}li>
ul>
data: {
person: {name: "张三", age: 11, gender: "男"},
}
当直接改变数组中的某个元素的值时,数组中的数据确实会改变,vue并不会把更改过后的数据渲染到界面上。例如: this.array[0]=3;
当使用数组中的方法去操作元素时,vue会更新数据。例如:array.push(3)
如果数组的元素是对象,改变对象的属性值,vue会将修改过后的数据渲染到界面上
如果想要添加修改删除数组中的元素,最好使用数组自带的方法。
在显示许多数据时,我们可能会去修改数据的格式,然后再去显示,我们可以使用过滤去来方便我们实现
语法: { {要处理的数据 | 过滤器名称}}
过滤器是一个方法,写在vue的filters属性中,参数即为你要处理的数据,返回值即为你处理之后的参数,方法名即为过滤器的名称
const app = new Vue({
el: "#app",
data: {
},
filters: {
getPrice (price) {
return "¥" + price.toFixed(2);
}
}
})
注意:过滤器与计算属性不同,过滤器是针对多个数据进行处理,是一个方法。计算属性是对已有数据进行处理,得到一个值,可看作一个变量。
v-model是用来双向绑定属性的。表单中input标签的值修改,data中绑定的数据跟着修改,绑定数据修改,input标签中的值也跟着修改
语法:{ {message}}
<div id="app">
<input type="text" v-model="message"/>{
{message}}
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "你好呀"
}
})
script>
当修改radio标签时,把radio设置的值写上去,让不同的radio使用v-model绑定相同的值,当某个radio被选中时,绑定的值会改变成该radio对应的值,当绑定的值改变时,该值对应的radio就会被选中。当绑定值有默认值时,页面的radio也会默认选中。
<label>
<input type="radio" value="男" v-model="value">
label>男
<label>
<input type="radio" value="女" v-model="value">
label>女
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "你好呀",
value: "男"
}
})
script>
当绑定的值的类型是布尔时,复选框选中即为true,不选中即为fasle
注意:只要绑定的值为布尔,不管绑定了多少个checkbox,value属性有没有值,都会是上面的状况,绑定的值为true所有复选框全选中,为false所有复选框全不选中
语法:
当绑定的值为数组类型时,某个复选框选中会把值添加到数组中,取消选中会去掉该元素,修改绑定数组也会取消选中对应的复选框
语法:
唱
跳
rap
篮球
绑定值与被选中的选项会双向绑定,绑定值类型应为option的value的类型
注意:这次绑定值决定不了绑定方式,决定绑定方式的是select标签有没有multiple属性
语法:
<label for="">
<select name="like" id="" v-model="like">
<option value="唱">唱option>
<option value="跳">跳option>
<option value="rap">rapoption>
<option value="篮球">篮球option>
select>
label>
绑定值与被选中的选项会双向绑定,绑定值类型应为数组
注意:select标签加了multiple属性
<label for="">
<select name="like" v-model="like" multiple>
<option value="唱">唱option>
<option value="跳">跳option>
<option value="rap">rapoption>
<option value="篮球">篮球option>
select>
label>
<input type="text"v-model.lazy="message">
<input type="number" v-model.number.lazy="age">
<input type="text" v-model.trim="name">
// 创建组件构造器对象
const cpn = Vue.extend({
template: `
我是标题
我是内容,哈哈哈哈
我是内容,呵呵呵呵
`
})
// 注册组件
Vue.component("my-cpn", cpn);
<my-cpn>my-cpn>
全局组件:能在所有vue挂载的标签中使用
注册方式:Vue.component("cpn", cpn)
局部组件只能在其对应的Vue挂载的标签中使用
注册方式: 创建vue实例时在他的componets属性中添加注册组件,属性名为组件名,值为组件对象
const app = new Vue({
el: "#app",
data: {
},
// 局部组件注册
components: {
cpn: cpn
}
})
在组件里也可以使用组件,使用的组件的组件叫做父组件,被使用的组件叫做子组件。
在组件compents属性中注册某个组件,就可以使用该组件。
注意,当我们使用组件时,我们不能使用子组件注册的孙组件,除非我们注册该组件
const cpn = Vue.extend({
template: `
我是标题
我是内容
`,
components: {
cpn2: cpn2
}
})
全局注册:不用写Vue.extend
Vue.component("cpn", {
template: `
我是标题
我是内容2
`
})
局部注册:
components: {
cpn: {
template: `
我是标题
我是内容2
`
}
}
<script type="text/x-template" id="cpn">
<div>
<h2>我是标题</h2>
<h4>我是内容1</h4>
</div>
script>
<template id="cpn">
<div>
<h2>我是标题h2>
<h4>我是内容1h4>
div>
template>
注意:这里只是把模板分离出来了,并没有把整个组件分离出来,以下是引用方式,
components: {
cpn: {
template: `#cpn`
}
}
下面是一个计数器小案例
<template id="cpn">
<div>
<div>现在的数字是:{
{number}}div>
<button @click="increment">+button>
<button @click="decrement">-button>
div>
template>
<script>
Vue.component("cpn", {
template: `#cpn`,
data() {
return {
number: 0
}
},
methods: {
increment() {
this.number++;
},
decrement() {
this.number--;
}
}
})
const app = new Vue({
el: "#app",
data: {
},
components: {
}
})
</script>
父组件通过给子组件添加属性来传值,子组件通过props属性来接收,这样子组件就可以像使用data里的数据来使用父组件传过来的值
props: ["movies"]
<template id="cpn">
<div>
<h2>{
{movies}}h2>
div>
template>
<div id="app">
<cpn :movies="movies">cpn>
div>
子组件可以指定传过来值的类型以及默认值是否必填等…
props: {
name: {
type: [String,Number],//指定多种类型
default: 0 //设置默认值
},
movies: {
type: Array,//指定类型
default() {
//默认值,数组或对象的默认值必须是方法的返回值
return ["海王"]
},
required: true //设置是否必填,其实设置默认值之后就没必要设置这个属性了
},
注意:父组件在传值时用的属性名不能用大写,如果props的属性名有大写,父组件传值的时候使用’-'代替 my-name <=> myName
子组件通过this.$emit方法来触发父组件添加到字标签上的自定义事件来进行通信
click(item) {
this.$emit("cpn-click", item);
}
methods: {
cpnClick(item) {
console.log(item)
}
},
在父组件的对象的$children是该组件所有的直接子组件的数组
console.log(this.$children);
我们想获取指定的子组件时,使用组件时,给组件添加ref=“子组件id”,this.$refs.子组件id 即为该组件对象
<cpn :msg="message" ref="mr3">cpn>
console.log(this.$refs.mr3);
通过子组件的$parent 、 $root 属性即可获取
注意: 开发中不建议这样使用,这会使父组件与组件之间耦合性很高,而我们使用组件就是为了降低代码之间的耦合性,提高代码复用率。
console.log(this.$parent);
console.log(this.$root);
现在我们的组件还不够灵活,我们只能传入数据,父组件并不能指定他有哪些内容,对于一些特别相似的但又有些不同模块,如果我们只能创建多个组件,不够灵活。
我们可以使用slot标签来解决这个问题,在组件中写入这个标签,父组件在使用该组件时,在组件中添加的内容就会代替slot标签。实现对组件内容的控制。
<template id="cpn">
<div>
<h2>我是标题h2>
<h4>我是内容h4>
<slot>slot>
div>
template>
<cpn>我是插槽cpn>
当然组件也可以设置给插槽添加默认值,使用组件时,组件标签中没有内容就会使用默认值。
<template id="cpn">
<div>
<h2>我是标题h2>
<h4>我是内容h4>
<slot>我是插槽/slot>
div>
template>
<cpn>cpn>
如果组件标签中有多个标签,所有内容会替代组件中的slot标签
之前我们一个组件只有一个插槽,一个插槽是不够灵活的,我们需要多插个插槽,为了能指定每个插槽要展示的东西,我们需要有一个标识,这个表示就是name,给插槽添加name,使用组件时根据不能的name添加不同的值
<template id="cpn">
<div>
<slot name="left">左边slot>
<slot name="center">中间slot>
<slot name="right">右边slot>
div>
template>
<cpn>
<div slot="left">我是左边div>
<div slot="center">我不是右边div>
<div slot="right">我既不是左边,也不是中间div>
cpn>
在父组件替换子组件的插槽时,使用子组件的数据
我们可以获取到子组件的实例,(上面提到过),_data属性就是子组件的data属性
还有另外的方法
<template id="cpn">
<div>
<slot :data="movies">slot>
div>
template>
<cpn ref="cpn">
<div slot-scope="slot">
<span>{
{slot.data}}span>
div>
cpn>
前端模块化开发即把每一个js文件当做一个模块,模块和模块之间的内容不会有任何的相互影响。
通过js的导出与导入来使用模块的变量、类、函数…
导入与导出的方法有许多规范:CommonJS、AMD、ES6… 不用的规范有不同的语法,不同的实现原理,但是理念都是一样的。
下面讲解ES6规范
const name = "张三";
const age = 12;
// export {
// 变量名,变量名
//};
export {
name,age
};
export const age = 12;
const name = "张三";
export default name;
// import {变量名,变量名} from "文件地址"
import {
name,age} from "./chu.js"
console.log(name)
console.log(age)
import * as test from "./chu.js"
console.log(test.name)
import test from "./chu.js"
console.log(test)
let和const只在es6中有,es5中没有
let与var相似,都是变量
const相当于java中的常量。
综上所述:我们在定义变量时,值不会改变的一律用const修饰,可能改变的用let修饰,不用var修饰。
前者等价于后者,前者写起来更简单
const name = "张三";
const age = 18;
const gender = true;
const obj = {
name,
age,
gender,
sayHello(){
console.log("hello")
}
}
const name = "张三";
const age = 18;
const gender = true;
const obj = {
name: name,
age: age,
gender: gender,
sayHello: function (){
console.log("hello")
}
}
根据参数的不同,这个函数可以对数组进行删除、添加、修改、插入
所做的流程是:删除该数组第index个值后删除num个元素,然后添加第三个参数即以后的元素
作用:创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,
传参:(callback(当前元素,索引,该数组));
返回值:通过测试的元素的集合的数组;
//标准用法
let arr = array.filter(callback(currentValue, index, array){
//do something
}, this)
作用:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
传参:(callback(当前元素,索引,该数组));
返回值:一个新数组,每个元素都是回调函数的结果;
//标准用法
let numbers = [1, 4, 9];
let roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3],
//numbers的值仍为[1, 4, 9]
作用:对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值;
传参:(callback(累加器accumulator,当前元素,索引,该数组));
返回值:函数累计处理的结果;
//标准用法
let total = [0, 1, 2, 3].reduce((sum, value) => {
return sum + value;
}, 0);
// total is 6
let flattened = [[0, 1], [2, 3], [4, 5]];
flattened.reduce((a, b) => {
return a.concat(b);
}, []);
// flattened is [0, 1, 2, 3, 4, 5]