Vue 3 从入门到精通(持续更新)

搭建开发与调试环境

在开发 Vue 前端页面之前,首先需要搭建开发和调试环境。

Vue.is的安装有4种方式:

  1. 使用 CDN 方式
  2. 使用 NPM 方式
  3. 使用命令行工具(Vue CLI)方式
  4. 使用 Vite 方式

1. 使用 CDN 方式

CDN(Content Delivery Network,内容分发网络)是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需的内容,降低网络堵塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。

使用CDN方式来安装Vue框架,就是选择一个提供稳定Vuejs链接的CDN服务商。选择CDN后,在页面中引入Vue的代码如下:

<script src="https://unpkg.com/vue@next"></script>

2. 使用 NPM 方式

NPM 是一个 Node.js 包管理和分发工具,也是整个 Node.js 社区最流行、支持第三方模块最多的包管理器。在安装 Node.js 环境时,安装包中包含 NPM,如果安装了 Node.js,则不需要再安装NPM。
用 Vue 构建大型应用时,推荐使用 NPM 安装。NPM 能很好地和诸如 Webpack 或 Browserify 模块打包器配合使用。

使用NPM安装 Vue.js 3.x:

#最新稳定版
npm install vue@next

由于国内访问国外的服务器非常慢,而 NPM 的官方镜像就是国外的服务器,为了节省安装时间,推荐使用淘宝 NPM 镜像 CNPM,在命令提示符窗口中输入下面的命令并执行:

npm install -g cnpm --registry=https://registry.npm.taobao.org

以后可以直接使用cnpm命令安装模块,代码如下:

cnpm install 模块名称

注意:通常在开发 Vue.js 3.x 的前端项目时,多数情况下会使用 Vue CLI 先搭建脚手架项目,此时会自动安装 Vue 的各个模块,不需要使用 NPM 单独安装Vue

3. 命令行工具(CLI)

Vue提供了一个官方的脚手架(Vue CLI),使用它可以快速搭建一个应用。搭建的应用只需要几分
钟的时间就可以运行起来,并带有热重载、保存时 lint 校验以及生产环境可用的构建版本。

例如想构建一个大型的应用,可能需要将应用分割成各自的组件和文件,此时便可以使用Vue CLI快速初始化工程。

因为初始化的工程可以使用 Vue 的单文件组件,它包含各自的 HTML、JavaScript 以及带作用域的 CSS 或者 SCSS ,格式如下:

<template>
HTML
</template>
<script>
JavaScript
</script>
<stvle scoped>
css 或者 SCSS
</style>

Vue CLI工具假定用户对 Node.js 和相关构建工具有一定程度的了解。如果是初学者,建议熟悉

Vue本身之后再使用 Vue CLI工具。本书后面的章节将具体介绍脚手架的安装以及如何快速创建个项目。

4. 使用 Vite 方式

Vite 是 Vue 的作者尤雨溪开发的 Web 开发构建工具,它是一个基于浏览器原生 ES 模块导入的开发服务器。在开发环境下,利用浏览器去解析 import,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随启随用。本文后面的章节将具体介绍 Vite 的使用方法。

指令

指令是Vue模板中最常用的一项功能,它带有前缀v,主要职责是当其表达式的值改变时,应地将某些行为应用在DOM上。本章将介绍Vue的内置指令,以及自定义指令的注册与使用。

1. 内置指令

内置指令顾名思义就是 Vue 内置的一些指令,它针对一些常用的页面功能提供了以指令来封装的使用形式,以 HTML 属性的方式使用。

1.1 v-show

v-show指令会根据表达式的真假值切换元素的 display CSS属性,来显示或者隐藏元素。当条件变化时,该指令会自动触发过渡效果。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-show指令</title>
</head>
<body>
<div id="app">
    <h3 v-show="ok">西瓜</h3>
    <h3 v-show="no">苹果</h3>
    <h3 v-show="num>=1000">库存很充足!</h3>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             ok:true,
             no:false,
             num:1000
           }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

在 Chrome 浏览器中运行程序,按 F12 键打开控制台并切换到 Elements 选项,展开<div>标签,可以发现,“苹果”并没有显示,这是因为 v-show 指令计算“no”的值为 false,所以元素不会显示。

在 Chrome 浏览器的控制台中可以看到,使用 v-show 指令,元素本身是被渲染到页面中的,只是通过 CSS 的 display 属性来控制元素的显示或者隐藏。如果 v-show 指令计算的结果为 false,则设置器样式为“display:none;”。

1.2 v-if/ v-else-if/ v-else

在 Vue 中,使用 v-if、v-else-if 和 v-else 指令实现条件判断。

1. v-if 指令

v-if 指令根据表达式的真假来有条件地渲染元素。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-if指令</title>
</head>
<body>
<div id="app">
    <h3 v-if="ok">冰箱</h3>
    <h3 v-if="no">洗衣机</h3>
    <h3 v-if="num>=1000">库存很充足!</h3>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             ok:true,
             no:false,
             num:1000
           }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

在 Chrome 浏览器中运行程序,按 F12 键打开控制台并切换到Elements选项,使用 v-if="no” 的元素并没有被渲染,使用 v-if="ok” 的元素正常渲染了。也就是说,当表达式的值为 false 时,v-if 指令不会创建该元素,只有当表达式的值为 true 时,v-if 指令才会真正创建该元素。这与 v-show 指令不同,v-show 指令无论表达式真假,元素本身都会被创建,显示与否是通过CSS的样式属性display来控制的。

一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

2. v-else-if/v-else 指令

v-else-if 指令与 v-if 指令一起使用,用法与 JavaScript 中的 if...else if类似。
下面的示例使用 v-else-if 指令与 v-if 指令判断学生成绩对应的等级。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-else-if指令与v-if指令</title>
</head>
<body>
<div id="app">
    <span v-if="score>=90">优秀</span>
    <span v-else-if="score>=80">合格</span>
    <span v-else-if="score>=60">及格</span>
    <span v-else>不及格</span>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             ok:true,
             no:false,
             score:85
           }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

在Chrome浏览器中运行程序,按 F12 键打开控制台并切换到 Elemcnts 选项,当满足其中一个条件后,程序就不会再往下执行。使用 v-else-if 和 v-else 指令时,它们要紧跟在 v-if 或者 v-else-if 指令之后。

1.3 v-for

使用 v-for 指令可以对数组、对象进行循环,以获取其中的每一个值。

1. 使用 v-for 指令遍历数组

使用 v-for 指令必须使用特定语法 alias in expression,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名,具体格式如下:

<div v-for = "item in items">
    {{item}}
</div>

下面看一个示例,使用 v-for 指令循环渲染一个数组。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-for指令遍历数组</title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="item in nameList">
          {{item.name}}--{{item.city}}--{{item.price}}元
        </li>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
                  nameList:[
                      {name:'洗衣机',city:'上海',price:'8600'},
                      {name:'冰箱',city:'北京',price:'6800'},
                      {name:'空调',city:'广州',price:'5900'}
                  ]
            }
         }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>



提示:v-for 指令的语法结构也可以使用 of 替代 in 作为分隔符,例如:

<li v-for="item of nameList">

在 v-for 指令中,可以访问所有父作用域的属性。v-for 还支持一个可选的第二个参数,即当前项的索引。例如,修改上面的示例,添加 index 参数,代码如下:

<ul>
    <li v-for="(item,index) in nameList">
        {{index}}---{{item.name}}--{{item.score}}分--{{item.class}}
    </1i>
</ul>

2. 使用 v-for 指令遍历对象

遍历对象的语法和遍历数组的语法是一样的:

value in object

其中 object 是被迭代的对象,value 是被迭代的对象属性的别名。
 

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-for指令遍历对象</title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="item in nameObj">
            {{item}}
        </li>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
            nameObj:{
                name:"洗衣机",
                city:"上海",
                price:"6800元"
             }
           }
         }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

还可以添加第二个参数,用来获取键值;要获取选项的索引,可以添加第三个参数。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>添加第二、三个参数</title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(item,key,index) in nameObj">
            {{index}}--{{key}}--{{item}}
        </li>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
            nameObj:{
                name:"洗衣机",
                city:"上海",
                price:"6800元"
             }
           }
         }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>
3. 使用 v-for 指令遍历整数

也可以使用 v-for 指令遍历整数。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-for指令遍历整数</title>
</head>
<body>
<div id="app">
    <span v-for="item in 20">
        {{item}}
    </span>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
     }).mount('#app');
</script>
</body>
</html>
4. 在<template>上使用 v-for

类似于 v-if,也可以利用带有 v-for 的<template>来循环渲染一段包含多个元素的内容。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>在<template>上使用v-for</title>
</head>
<body>
<div id="app">
    <ul>
        <template  v-for="(item,key,index) in nameObj">
            <li>{{index}}--{{key}}--{{item}}</li>
        </template>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
        data(){
          return{
            nameObj:{
                name:"洗衣机",
                city:"上海",
                price:"6800元"
             }
           }
         }
     }).mount('#app');
</script>
</body>
</html>

提示:template元素一般常和v-for和v-if结合使用,这样会使得整个HTML结构没有那么多多余的元素,整个结构会更加清晰。

5. 数组更新检测

Vue 将被监听的数组的变异方法进行了包裹,它们也会触发视图更新。被包裹过的方法包括push()、pop()、shift()、unshit()、splice()、sort() 和 reverse()。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>数组更新检测</title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(item,index) in nameList">
            {{index}}--{{item}}
        </li>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
        data(){
          return{
            nameList:["洗衣机","上海","5800元"]
           }
         }
     }).mount('#app');
通过索引向数组nameList添加“1800台”
vm.nameList[3]="1800台";
</script>
</body>
</html>

还有一些非变异方法,例如 filter()、concat() 和 slice()。它们不会改变原始数组,总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>数组更新检测</title>
</head>
<body>
<div id="app">
    <ul>
        <li v-for="(item,index) in nameList">
            {{index}}--{{item}}
        </li>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
        data(){
          return{
            nameList:["洗衣机","上海","5800元"]
           }
         }
     }).mount('#app');
//使用数组原型的splice()方法
vm.nameList.splice(0,0,"畅销版");

</script>
</body>
</html>
6. key 属性

当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且满保它们在每个索引位置正确渲染。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,需要为每项提供一个唯一 key 属性。

下面我们先来看一下不使用 key 属性的一个示例。定义一个 nameList 数组对象,使用 v-for 指令渲染到页面,同时添加三个输入框和一个添加的按钮,可以通过按钮向数组对象中添加内容。在示例中定义一个 add 方法,在方法中使用unshift()数组的开头添加元素。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>不使用key属性</title>
</head>
<body>
<div id="app">
    <div>名称:<input type="text" v-model="names"></div>
    <div>产地:<input type="text" v-model="citys"></div>
    <div>价格:<input type="text" v-model="prices"><button v-on:click="add()">添加</button></div>
    <hr>
    <p v-for="item in nameList">
    <input type="checkbox">
    <span>名称:{{item.name}}—产地:{{item.city}}—价格:{{item.price}}</span>
</p>

</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
        data(){
          return{
            names:"",
            citys:"",
            prices:"",
            nameList:[
                {name:'洗衣机',city:'北京',price:'6800元'},
                {name:'冰箱',city:'上海',price:'8900元'},
                {name:'空调',city:'广州',price:'6800元'}
            ]
           }
         },
        methods:{
            add:function(){
                this.nameList.unshift({
                    name:this.names,
                    city:this.citys,
                    price:this.prices
                })
            }
        }
     }).mount('#app');
</script>
</body>
</html>

在Chrome浏览器中运行程序,选中列表中的第一个选项,然后在输入框中输入新的内容,单击“添加”按钮后,向数组开头添加一组新数据,页面中也相应显示,但是刚才选择的“洗衣机”变成了新添加的数据。很显然,这不我们想要的结果。

产生这种结果的原因就是 v-for 指令的“就地更新”策略,只记住了数组勾透意的索引0。当往数组中添加内容的时候,虽然数组长度增加了,但是指令只记得刚开始选择的数下标,于是就选择了新数组中下标为0的选项。

为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,需到每项提供一个唯一 key 属性。

修改上面的示例,在 v-for 指令的后面添加 key 属性,代码如下:

<p v-for="item in nameList" v-bind:key="item.name">
<input type="checkbox">
<span>name:{{item.name}}, score:{{fitem.score}},class:{{item.class}}</span>


此时重复上面的操作,可以发现已经实现了想要的结果。

7. 过滤与排序

在实际开发中,可能一个数组需要在很多地方使用,有些地方是过滤后的数据,而有些地方是重新排列的数组。这种情况下,可以使用计算属性或者方法来返回过滤或排序后的数组。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>过滤与排序</title>
</head>
<body>
<div id="app">
    <p>所有库存的商品:</p>
    <ul>
        <li v-for="item in nameList">
            {{item}}
        </li>
    </ul>
    <p>产地为上海的商品:</p>
    <ul>
        <li v-for="item in namelists">
            {{item}}
        </li>
    </ul>
    <p>价格大于或等于5000元的商品:</p>
    <ul>
        <li v-for="item in prices()">
            {{item}}
        </li>
    </ul>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
        data(){
          return{
            nameList:[
                {name:"洗衣机",price:"5000",city:"上海"},
                {name:"冰箱",price:"6800",city:"北京"},
                {name:"空调",price:"4600",city:"深圳"},
                {name:"电视机",price:"4900",city:"上海"}
            ]
           }
         },
        computed:{  //计算属性
            namelists:function(){
                return this.nameList.filter(function (nameList) {
                    return nameList.city==="上海";
                })
            }
        },
        methods:{  //方法
            prices:function(){
                return this.nameList.filter(function(nameList){
                    return nameList.price>=5000;
                })
            }
        }
     }).mount('#app');
</script>
</body>
</html>
8. v-for与v-if一同使用

v-for 与 v-if 一同使用,当它们处于同一节点上时,v-for 的优先级比 v-if 更高,这意味着将分别重复运行于每个 v-for 循环中。当只想渲染部分列表选项时,可以使用这种组合方式。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>v-for与v-if一同使用</title>
    </head>
    <body>
        <div id="app">
            <h3>已经出库的商品</h3>
            <ul>
                <template v-for="goods in goodss">
                    <li v-if="goods.isOut">
                        {{goods.name}}
                    </li>
                </template>
            </ul>
            <h3>没有出库的商品</h3>
            <ul>
                <template v-for="goods in goodss">
                    <li v-if="!goods.isOut">
                        {{goods.name}}
                    </li>
                </template>
            </ul>
        </div>
<script src="vue.global.js"></script>
    <script>
        const vm = Vue.createApp({
            data() {
                return {
                    goodss: [
                        {name: '洗衣机', isOut: false},
                        {name: '冰箱', isOut: true},
                        {name: '空调', isOut: false},
                        {name: '电视机', isOut: true},
                        {name: '电脑', isOut: false}
                    ]
                }
            }
        }).mount('#app');
    </script>
  </body>
</html>

1.4 v-bind

v-bind 指令主要用于响应更新 HTML 元素的属性,将一个或多个属性或者一个组件的 prop 动态绑定到表达式。

在下面的例子中,将按钮的 title 和 style 属性通过 v-bind 指令进行绑定,这里对于样式的绑定,需要构建一个对象。其他的对于样式的绑定方法,将在后面的章节中详细介绍。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-bind指令</title>
</head>
<body>
<div id="app">
    <input type="button" value="按钮" v-bind:title="Title" v-bind:style="{color:Color,width:Width+'px'}">
    <p><a :href="Address">超链接</a></p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
         data(){
             return {
                Title: '这是我自定义的title属性',
                Color: 'blue',
                Width: '100',
                Address:"https://www.baidu.com/"
           }
        }
     }).mount('#app');
</script>
</body>
</html>

1.5 v-model

v-model 指令用来在表单<input>、<textarea>及<select>元素上创建双向数据绑定,它会根据熟类型自动选取正确的方法更新元素。它负责监听用户的输入事件以及更新数据,并对一些极端场景进行特殊处理。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-bind指令</title>
</head>
<body>
<div id="app">
    <!--使用v-model指令双向绑定input的值和test属性的值-->
    <p><input v-model="content" type="text"></p>
    <!--显示content的值-->
    <p>{{content}}</p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
         data(){
             return {
                content: "空调"
           }
        }
     }).mount('#app');
</script>
</body>
</html>

从上面这个示例可以了解Vue的双向数据绑定,关于 v-model 指令的更多使用方法,后面的章节中还会详细讲解。

1.6 v-on

v-on 指令用于监听 DOM 事件,当触发时运行一些 JavaScript 代码。v-on 指令的表达式可以是一般的 JavaScript 代码,也可以是一个方法的名字或者方法调用语句。在使用 v-on 指令对事件进行绑定时,需要在 v-on 指令后面接上事件名称,例如 click、mousedown、mouseup等事件。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-on</title>
</head>
<body>
<div id="app">
    <p>
        <!--监听click事件,使用JavaScript语句-->
        <button v-on:click="number-=1">-1</button>
        <span>{{number}}</span>
        <button v-on:click="number+=1">+1</button>
    </p>
    <p>
        <!--监听click事件,绑定方法-->
        <button v-on:click="say()">古诗</button>
    </p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             number:100
           }
        },
        methods:{
            say:function(){
                alert("曲水浪低蕉叶稳,舞雩风软纻罗轻。")
            }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

在 Vue 应用中,许多事件的处理逻辑会很复杂,所以直接把 JavaSeript 代码写在 v-on 指令中是不可行的,此时就可以使用 v-on 接收一个方法,把复杂的逻辑放到这个方法中。
提示:使用 v-on 指令接收的方法名称也可以传递参数,只需要在 methods 中定义方法时说
明这个形参,即可在方法中获取到。

1.7 v-text

v-text 指令用来更新元素的文本内容。如果只需要更新部分文本内容,可使用插值来完成。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-text</title>
</head>
<body>
<div id="app">
    <!--更新部分内容-->
    <p>古诗欣赏:{{message}}</p>
    <!--更新全部内容-->
    <p v-text="message"></p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             message: '百舌无言桃李尽,柘林深处鹁鸪鸣。'
           }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

1.8 v-html

y-html 指令用于更新元素的 innerHTML。内容按普通 HTML 插入,不会作为 Vue 模板进行编译。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-html</title>
</head>
<body>
<div id="app">
        <p v-html="message"></p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             message:'<h3 style="color:red"> 老去惜花心已懒,爱梅犹绕江村。</h3>'
            }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

注意:在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只可以在可信内容上使用 v-html,禁止在用户提交的内容上使用 v-html 指令。

1.9 v-once

v-once 指令不需要表达式。v-once 指令只渲染元素和组件一次,随后的渲染使用了此指令的元素、组件及其所有的子节点,都会当作静态内容并跳过,这个特点可以用于优化更新性能。

例如,在下面的示例中,当修改 input 输入框的值时,使用了 v-once 指令的p元素不会随之变,而第二个p元素会随着输入框的内容而改变。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-once</title>
</head>
<body>
<div id="app">
    <p v-once>不可改变:{{message}}</p>
    <p>可以改变:{{message}}</p>
    <p><input type="text" v-model = "message" name=""></p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             message:"苹果"
            }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

1.10 v-pre

v.pre 指令不需要表达式,用于跳过这个元素和它的子元素的编译过程。可以使用 v-pre 指令来显示原始 Mustache 标签。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-pre</title>
</head>
<body>
<div id="app">
    <div v-pre>{{message}}</div>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             message:"竹根流水带溪云。醉中浑不记,归路月黄昏。"
            }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

1.11 v-cloak

v-cloak 指令不需要表达式。这个指令保持在元素上直到关联实例结束编译。和CSS规则(如[v-cloak]{display:none}) 指令一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-cloak</title>
    <!-- 添加 v-cloak 样式 -->
    <style>
        [v-cloak] {
            display: none;
        }
    </style>
</head>
<body>
<div id="app">
      <p v-cloak>{{message}}</p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({
        //该函数返回数据对象
        data(){
          return{
             message:"竹根流水带溪云。醉中浑不记,归路月黄昏。"
            }
        }
        //在指定的DOM元素上装载应用程序实例的根组件
     }).mount('#app');
</script>
</body>
</html>

2. 自定义指令

自定义指令可以用来操作 DOM。尽管 Vue 使用数据驱动视图的理念,但并非所有情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅可用于定义任何 DOM 操作,并且是可复用的。在 Vue 中,除了核心功能默认内置的指令外,Vue 也允许注册自定义指令。一般情况下普通DOM 元素进行底层操作,就会用到自定义指令。

1. 注册自定义指令

自定义指令的注册方法和组件很像,也分全局注册和局部注册,例如注册一个 v-focus 指令用于在<input>、<textarea>元素初始化时自动获得焦点,全局注册的写法如下:

//全局注册
const app = Vue.createApp({});
app.directive('focus',{
    //指令选项
});

局部注册的写法如下:

//局部注册
const app = Vue.createApp({
    directives:{
        focus:{
        //指令选项
        }
    }
}).mount('#app');

然后可以在模板中任何元素上使用新的 v-focus 指令,例如:

<input v-focus>

2. 钩子函数

自定义指令在 directives 选项中实现,directives选项中提供了以下钩子函数,这些钩子函数是可选的。

(1) beforeMount:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。

(2) mounted:在挂载绑定元素的父组件时调用

(3) beforeUpdate:指令所在组件的VNode更新前调用

(4) update:指令所在组件的VNode及其子组件的VNode全部更新后调用

(5) beforeUnmount:在卸载绑定元素的父组件之前调用

(6) unmount:只调用一次,指令与元素解绑且父元素已卸载时调用

可以根据需求在不同的钩子函数内完成逻辑代码,例如,上面的 v-focus 指令,希望在元素插入父节点时就可以调用,最好使用mounted选项。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>v-focus</title>
    </head>
<body>
<div id="app">
    <input v-focus>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({  });
    // 注册一个全局自定义指令 `v-focus`
    vm.directive('focus', {
        //当被绑定的元素插入到DOM中时
        mounted: function (el) {
            // 聚焦元素
            el.focus()
        }
    })
    vm.mount('#app');
</script>
</body>
</html>

在 Chrome 浏览器中运行程序,可以看到,页面在 v-focus 完成时,输入框自动获取焦点。

每个钩子函数都有几个参数可用,例如上面用到的el。它们的含义如下:

(1) el:指令所绑定的元素,可以用来直接操作 DOM。

(2) binding:一个对象,包含以下属性:

  • name:指令名,不包括“v-”前缀
  • value:指令的绑定值,例如v-my-directive = "1+1”,value的值是2
  • oldValue:指令绑定的前一个值,仅在 upadte 和componentUpdated钩子中可用,无论值是否改变都可用。
  • expression:绑定值的字符串形式。例如 v-my-directive="1+1",expression的值是"1+1"
  • arg:传给指令的参数。例如 v-my-directive:foo,arg 的值是foo.
  • modifiers:一个包含修饰符的对象。例如 v-my-diretive.foo.bar,修体符对象modifiers的值是{foo:true,bar:true}。

(3)vnode:Vue 编译生成的虚拟节点。

(4) oldVnode:上一个虚拟节点,仅在 update 和 beforeUpdate 钩子中可用。

注意:除了el之外,其他参数都应该是只读的,切勿进行修改。如果需要在钩子之间共数数据,则建议通过元素的 dataset 来进行。

下面自定义一个指令,在其钩子函数中输入各个参数。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>bind钩子函数的参数</title>
    </head>
<body>
<div id="app">
    <div v-demo:foo.a.b="message"></div>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
     //该函数返回数据对象
        data(){
            return{
                 message: '海上生明月'
           }
        }
     })
     // 注册一个全局自定义指令 `demo`
    vm.directive('demo', {
        mounted (el, binding, vnode) {
            let s = JSON.stringify
            el.innerHTML =
    	 'instance: '   + s(binding.instance) + '<br>' +
    	 'value: '      + s(binding.value) + '<br>' +
    	 'argument: '   + s(binding.arg) + '<br>' +
    	 'modifiers: '  + s(binding.modifiers) + '<br>' +
    	 'vnode keys: ' + Object.keys(vnode).join(', ')
        }
    })
    vm.mount('#app');
</script>
</body>
</html>

在 Chrome 浏览器中运行程序,由于将钩子函数的参数信息赋值给了<div>元素的 innerHTML 属性,因此会在页面中显示钩子函数的参数信息。

3. 动态指令参数

自定义的指令可以使用动态参数。例如,在v-pin:[direction]="value”中,direction参数可以根据组件实例数据进行更新,从而可以更加灵活地使用自定义指令。

下面的例子通过自定义指令来实现一个功能:让某个元素固定在页面中某个位置,在出现滚动条时,元素不会随着滚动条而滚动。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>动态指令参数</title>
    </head>
<body>
<div id="app">
    <!--直接给出指令的参数-->
   <p v-dong:top="100">林风纤月落</p>
   <!--使用动态参数-->
   <p v-dong:[direction]="100">林风纤月落</p>
</div>
<!--引入vue文件-->
<script src="vue.global.js"></script>
<script>
    //创建一个应用程序实例
    const vm= Vue.createApp({ 
     //该函数返回数据对象
        data(){
            return{
                 direction: 'left'
           }
        }
     })
     // 注册一个全局自定义指令 `dong`
    vm.directive('dong', {
         beforeMount(el, binding, vnode) {
	        el.style.position = 'fixed';
	        let s = binding.arg || 'left';
            el.style[s] = binding.value + 'px'
        }
    })
    vm.mount('#app');
</script>
</body>
</html>

3. 综合案例 - 通过指令实现下拉菜单效果

网站主页中经常需要设计下拉菜单,当鼠标移动到某个菜单上时会弹出下拉菜单列表,每个单项可以链接到不同的页面,当鼠标离开菜单列表时,菜单项会被隐藏。下面通过指令来设计这样的下拉菜单效果。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>下拉菜单</title>
<style>
			body {
				width: 600px;
			}
			a {
				text-decoration: none;
				display: block;
				color: #fff;
				width: 120px;
				height: 40px;
				line-height: 40px;
				border: 1px solid #fff;
				border-width: 1px 1px 0 0;
				background: #5D478B;
			}
			li {
				list-style-type: none;
			}
			#app > li {
				list-style-type: none;
				float: left;
				text-align: center;
				position: relative;
			}
			#app li a:hover {
				color: #fff;
				background: #FF8C69;
			}
			#app li ul {
				position: absolute;
				left: -40px;
				top: 40px;
				margin-top: 1px;
				font-size: 12px;
			}
			[v-cloak] {
				display: none;
			}
		</style>
	</head>
	<body>
		<div id = "app" v-cloak>
			<li v-for="menu in menus" @mouseover="menu.show = !menu.show" @mouseout="menu.show = !menu.show">
				<a :href="menu.url" >
					{{menu.name}}
				</a>
				<ul v-show="menu.show">
					<li v-for="subMenu in menu.subMenus">
						<a :href="subMenu.url">{{subMenu.name}}</a>
					</li>
				</ul>
			</li>
		</div>
	
		<script src="vue.global.js"></script>
		<script>
		    const data = {
		      menus: [
		      	{
		      		name: '在线课程', url: '#', show: false, subMenus: [
		      				{name: 'Python课程', url: '#'},
		      				{name: 'Java课程', url: '#'},
		      				{name: '前端课程', url: '#'}
		      			]
		      	},
		      	{
		      		name: '经典图书', url: '#', show: false, subMenus: [
		      				{name: 'Python图书', url: '#'},
		      				{name: 'Java图书', url: '#'},
		      				{name: '前端图书', url: '#'}
		      			]
		      	},
		      	{
		      		name: '技术支持', url: '#', show: false, subMenus: [
                            {name: 'Python技术支持', url: '#'},
		      				{name: 'Java技术支持', url: '#'},
		      				{name: '前端技术支持', url: '#'}
		      			]
		      	},
		      	{
		      		name: '关于我们', url: '#', show: false, subMenus: [
                            {name: '团队介绍', url: '#'},
		      				{name: '联系我们', url: '#'}
		      			]
		      	}
		      ]
		    };
		    const vm = Vue.createApp({
                data() {
                    return data;
                }
            }).mount('#app');
</script>
</body>
</html>

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海绵不是宝宝817

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值