VUE – 6-1 VUE 启动

Introduce

使用

使用 vue 的两种方式:

  • 引入 vue 的 js 文件
  • 脚手架
    • 官方脚手架 vue-cli
    • 民间脚手架 如 webpack-simple
    • 手动搭建

Hello!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <!-- 界面模板 -->
        <!-- mustache: {{js 表达式}} -->
        <h1>This is title: {{title}}</h1>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        // vm: Vue 实例
        let vm = new Vue({// 配置
            // el(element): css 选择器
            el: '#app',
            // data: 和界面相关的数据
            data: {
                message: 'Hello Vue.js!',
                title: 'Hello!'
            }
        })
    </script>
</body>
</html>

在开发者控制台中可通过变量访问并修改内容。

Pasted image 20240705231651.png

数据变动,VUE感知到数据变化,重渲染(数据响应式)。
Pasted image 20240705231802.png

MVVM(Model,View,View Model)模式

creeper!

<div id="app">
        <!-- 界面模板 -->
        <!-- mustache: {{js 表达式}} -->
        <h1>{{message}}</h1>
        <ul>
            <li v-for="(creep, i) in lists">
                {{creep.role}}: 
                <span v-if="creep.num > 0">{{creep.num}}</span>
                <span v-else>none</span>
                <button @click="spawn(i)">spawn</button>
                <button @click="kill(i)">kill</button>
            </li>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        // vm: Vue 实例
        let vm = new Vue({// 配置
            // el(element): css 选择器
            el: '#app',
            // data: 和界面相关的数据
            data: {
                message: 'Hello Vue.js!',
                lists: [
                    {role: 'harvester', num: 2},
                    {role: 'builder', num: 3},
                    {role: 'upgrader', num: 4}
                ]
            },
            methods: {
                // 方法
                spawn(i) {
                    // this 指代 vm 对象
                    this.lists[i].num++;
                },
                kill(i) {
                    this.lists[i].num--;
                    if (this.lists[i].num < 0) {
                        this.lists[i].num = 0;
                    };
                }
            }
        })
    </script>

效果如下
Pasted image 20240705234354.png

基础概念

ES6 一些特性

速写属性 和 速写方法

let role = harvest;
let creep = {
    role,
    logic() {
    }
}
/* 
let creep = {
    role: role,
    logic: function() {
    }
}
*/

模板字符串

let num = 1;
let str = `第一行
第二行
第三行${num}`;

// let str = '第一行\n第二行\n第三行' + num;

箭头函数

匿名函数都可写为箭头函数...
箭头函数没有自己的this,使用箭头函数定义位置的this

模块化

常见标准:CommonJS、ES6 Module、AMD、CMD、UMD

注入

之前的代码如下:

// vm: Vue 实例
let vm = new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue.js!',
        lists: [
            {role: 'harvester', num: 2},
            {role: 'builder', num: 3},
            {role: 'upgrader', num: 4}
        ]
    },
    methods: {
        // 方法
        spawn(i) {
            // this 指代 vm 对象
            this.lists[i].num++;
        },
        kill(i) {
            this.lists[i].num--;
            if (this.lists[i].num < 0) {
                this.lists[i].num = 0;
            };
        }
    }
})

我们可以直接通过vm.message或vm.spawn(NUM)访问data或methods中的属性。
因为data和methods中的内容会被直接提取到vue实例中。
该过程称之为注入。
目的如下:

  • 完成数据响应式
    将使用Object.definePropertyclass Proxy添加对数据的监听。
    区别->
  • 绑定this
    我们在methods中的函数中使用了this,方便函数修改读取数据。

虚拟 DOM 树

为了提高渲染效率,vue将模板编译成为虚拟DOM树,然后再生成真实DOM树。
虚拟DOM树只是js对象,并未展示到页面上,真实DOM树也是js对象,但和页面上的信息相关。

如何提高渲染效率的:

  • 减少直接操作真实DOM: 真实DOM操作是相对昂贵的,因为每次更改都会引起浏览器的重排或重绘。虚拟DOM充当了一个中间层,将对DOM的更改聚集在一起,并使用高效的算法来最小化实际DOM的操作次数,减少性能开销。
  • 批量更新: 虚拟DOM将多个DOM更改批量处理,这样可以避免多次重新渲染。在更新过程中,虚拟DOM会记录所有需要更新的更改,然后一次性进行实际DOM的操作,优化整体性能。
  • Diff算法: 虚拟DOM使用Diff算法比较先前虚拟DOM树和当前虚拟DOM树的差异。Diff算法能够高效地找出哪些部分发生了变化,然后只更新发生变化的部分到真实DOM,而不会重新渲染整个DOM树。
  • 内存中的操作: 虚拟DOM存在于JavaScript内存中,操作更加高效。相比之下,实际DOM操作涉及到与浏览器的交互,需要消耗更多的资源。
  • 跨平台优势: 虚拟DOM可以在不同平台上运行,例如浏览器和服务器端(例如React使用的React Native和React Server)。这种一致性让开发者可以在不同环境下共享代码,从而提高开发效率和性能。

可通过vm._vnode访问虚拟DOM树 pe(Pseudo Element)

let v1 = vm._vnode;
vm.title = "123";
let v2 = vm._vnode;

v1 === v2;
// false
v1.elm === v2.elm;
// true

因此,对于提升vue效率重点在以下两个方面:

  • 减少新的虚拟DOM树生成。
  • 保证对比之后,只有必要的节点变化。
    vue提供了以下方式修改虚拟DOM树:
  • 直接修改挂载的元素内部,使用元素的outerHTML作为模板。
  • 在template配置中书写。
  • 在render配置中使用函数直接创建虚拟节点树,完全脱离模板,省略编译步骤。
    从上到下优先级递增。
    上面的三个方法其实本质相同:
<div id="app">
    <<h1>{{message}}</h1>
    <ul>
        <li v-for="(creep, i) in lists">
            {{creep.role}}
        </li>
    </ul>
</div>
// vm: Vue 实例
let vm = new Vue({
    el: '#app',
    template: `
       <div>
             <h1>{{message}}</h1>
             <ul>
                 <li v-for="(creep, i) in lists">
                     {{creep.role}}
                 </li>
             </ul>
         </div>
    `,
    render(h) {
        return h("div", [
            h("h1", this.message),
            h("ul", this.lists.map(creep => h("li", creep.role)))
        ])
    },

    data: {
        ...
    },
    methods: {
        ...
    }
})

本质都是通过render函数。数据变化便可重新调用render函数将页面更新。

虚拟节点树必须是单根的
“模板只能由一个根节点”

挂载

将虚拟DOM树生成的真实DOM树,放置到某个元素位置(替换),称为挂载。
挂载的方式:

  • el配置
  • 通过调用vm.$mount("css选择器")

完整流程

首次渲染:实例创建 -> 注入 -> 编译生成虚拟DOM树 -> 挂载 -> 已挂载
重新渲染:数据变动 -> 重新生成新DOM树 -> 对比新旧树的差异 -> 将差异应用到真实DOM -> 完成渲染

模板语法 与 计算属性

模板语法

内容

元素内容位置
元素内容中使用mustache模板引擎进行解析
vue将vue实例绑定到模板字符串上,{{title}} 相当于 vm.title。

模板引擎
将一个模板字符串,加上一个对象,进行编译得到一个完整字符串。

指令

元素属性位置
指令影响元素的渲染行为,始终以v-开头
基础指令:

  • v-for: 循环渲染元素
  • v-html: 设置元素的innerHTML,会导致元素的模板内容失效
  • v-on: 注册事件
    • 十分常用可简写为@
    • 支持一些指令修饰符,如prevent阻止默认行为,stop阻止冒泡
    • 自动传递参数
  • v-bind: 绑定动态属性
    • 简写为:
  • v-show: 控制元素可见度
  • v-if v-else-if v-else: 控制元素生成
  • v-model: 双向数据绑定
    • v-onv-bind 的复合版
<li v-for="(creep, i) in lists">{{creep}}</li>

// html: "<p style='color:red'> Hello! </p>"
<h1 v-html="html"></h1>

<button v-on:click="spawn(i)">spawn</button>
<button @click="spawn(i)">spawn</button>

// 阻止默认行为
<a herf="" @click.prevent="console.log"></a>
// 点击时运行console.log(e), 参数自动传递
// 手动传递
<a herf="" @click.prevent="console.log($event)"></a>

// imgUrl: "https://xxx"
<img v-bind:src="imgUrl"/>
<img :src="imgUrl"/>

// isShow: true
<img v-show="isShow" :src="imgUrl"/>
<button @click="isShow = !isShow"></button>

<img v-if="isShow" :src="imgUrl"/>

<h1>{{text}}<h1>
<input type="text" :value="text" @input="text=$event.target.value"></input>

<h1>{{text}}<h1>
<input type="text" v-model:value="text"></input>

v-on中传递的可以是函数名也可以是函数调用

v-showv-if ... 的区别
v-show 只设置 display:none,元素依旧存在
v-if ... 不存在就真的不存在,不会出现在DOM中
若需要频繁切换显示,使用v-show不会影响元素结构

其他指令

  • v-slot
  • v-text
  • v-pre
  • v-cloak
  • v-once
  • 自定义指令

特殊属性

最重要的特殊属性 key
考虑以下代码:

<div id="app">
    <div v-if="loginMethod==='phone'">
        <label>手机号</label>
        <input type="text" />
    </div>
    <div v-else>
        <label>邮箱</label>
        <input type="text" />
    </div>
    <button @click="loginMethod === 'phone' ? loginMethod = 'email' : loginMethod = 'phone'">切换登录方式</button>
</div>

如果我们尝试在input中输入内容后切换登录方式,会发现输入框的内容依旧保留,但观察我们的代码两个并不是同一个输入框。
diff算法通过比较前两个DOM发现只有label发生变化,便只改动了label,导致input并没有发生任何变化。
key属性可以干预diff算法,若设置了key值,则会只对key相同的节点进行比对,若key值不同,便不再比对元素,直接修改(旧节点移除,新节点添加)。

<div id="app">
    <div v-if="loginMethod==='phone'">
        <label>手机号</label>
        <input type="text" key="1"/>
    </div>
    <div v-else>
        <label>邮箱</label>
        <input type="text" key="2"/>
    </div>
    <button @click="loginMethod === 'phone' ? loginMethod = 'email' : loginMethod = 'phone'">切换登录方式</button>
</div>

这样就不会出现上面的情况了。
再考虑以下情况:)

<div id="app">
    <p v-for="(v, i) in lists">
        {{v}}
        <button @click="lists.splice(i, 1)">delete</button>
    </p>
</div>

通过开发者控制台可以发现,当我们删除其中一个元素时,发现其他没被删除的元素也被更新了,若列表较长,会非常影响效率。
假设原来有1、2、3、4四个节点,若删除第二个1、3、4,比较时,1和1比较,2和3比较,3和4比较...发现后面的节点都变化了
针对这种情况,vue强烈建议给每个在循环生成的节点上添加一个唯一且稳定的key值。
添加后,只会比较key相同的节点。

<div id="app">
    <p v-bind:key="v.id" v-for="(v, i) in lists">
        {{v.value}}
        <button @click="lists.splice(i, 1)">delete</button>
    </p>
</div>

key属性不会出现在实际dom中

其他特殊属性

  • ref
  • is
  • slot
  • slot-scope
  • scope

计算属性

类似 getter setter

computed: {
    // 仅访问器
    prop() {
        return ...
    }
    // 访问器 + 设置器
    fullProp: {
        get() {
            return ...
        },
        set(val) {
            ...
        }
    }
}

例:

<div id="app">
        <p>firstname: {{firstName}}</p>
        <p>lastname: {{lastName}}</p>
        <p>fullname: {{fullName}}</p>
        <input type="text" v-model="fullName"/>
    </div>
    <script>
        let vm = new Vue({
            el: '#app',
            data: {
                firstName: "Lorem",
                lastName: "ipsum"
            },
            computed: {
                fullName: {
                    get() {
                        return this.firstName + " " + this.lastName;
                    },
                    set(val) {
                        console.log("1");
                        [this.firstName, this.lastName] = val.split(" ");
                    }
                }
            }
        })
    </script>

计算属性(computed)和方法(method)的区别

  • 计算属性可以赋值,方法不行
  • 计算属性会进行缓存,依赖不变,不会重新计算
  • 根据已有数据得到新数据的无参函数,都应该写成计算属性

组件

组件概念

直接将完整网页作为整体开发,会遇到以下困难

  • 代码凌乱臃肿
  • 不易协作
  • 难以复用
    推荐组件化开发,组件化就是把页面中的区域功能细分,每个区域成为一个组件,每个组件包含:
  • 功能:JS
  • 内容:模板
  • 样式:CSS

CSS代码需要构建工具的支撑

组件开发

创建组件

组件根据普通的配置对象创建,只需写一个配置对象即可开发一个组件
配置对象和vue实例几乎一样

// 组件配置对象
let com = {
    data() {
        return {
            // ...
        }
    },
    methods: {
        // ...
    },
    computed: {
        // ...
    },
    template: ...
}

组件配置对象和vue实例有以下差异:

  • 无el
  • data必须是一个函数,函数返回的对象作为数据
  • 没有el配置,组件的虚拟DOM树必须定义在template或render中

注册组件

两种注册方式,全局注册,局部注册

全局注册

整个应用中任何地方都可以使用该组件
全局注册方式:

// 参数1:组件名称,使用组件时的名称
// 参数2:组件配置对象
// 运行后便可在模板中使用组件
Vue.component("MyComp", myComp)
// 使用
<MyComp />
<MyComp></MyComp>
// 或
<my-comp />
<my-comp></my-comp>

大多数时候不需要全局注册,一般不建议

局部注册

在哪用在哪注册

let vm = new Vue({
    el:...
    ...
    components {
        // 属性名为组件名,属性值为组件配置对象
        MyComp: myComp,
    },
    template: `<div>
            <my-comp></my-comp>
        </div>`
})

应用组件

将组件名当作HTML元素名使用即可

  1. 组件必须有结束
    可以自结束,也可使用结束标记
  2. 组件的命名
    可以使用kebab-case短横线命名法,也可使用PascalCase大驼峰命名法
    若使用大驼峰命名法,使用时可以使用两种组件名

实际上,使用小驼峰命名法camelCase也可以识别,不过不符合官方要求的规范

组件树

一个组件创建好后,可能被多次使用,便会形成组件树

向组件传递数据

let ChildComp = {
    props: ["msg"],
    template: `
    <h2>{{ msg || 'No props passed yet' }}</h2>
    `
}
let vm = new Vue({
    el: ...
    components: {
        ChildComp,
    }
    template: `<div>
            <child-comp msg="Hello child" />
        </div>`
})

:msg 和 msg
若使用 :msg 即 v-bind:msg 时,引号内部被当作js代码传递
不使用 v-bind 则当作字符串传递

单向数据流
在组件中,属性只读,绝不可以更改

工程化

TODOList
├─ index.html
├─ lib
│  └─ vue.js
└─ src
   ├─ App.js
   ├─ components
   │  └─ TodoLists.js
   └─ main.js
啊哈,这里是小尾巴~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇