本周做项目用到了 vue,周末复习整理一下~
一、Vue.js 介绍
1.1 认识 Vue
- 一套用于构建用户界面的渐进式 JavaScript 框架
- 在项目中一点点来引入和使用 vue
- 大部分是用 vue 开发整个项目
- 全称是 vue.js
- 基于标准 HTML.CSS.JavaScript 构建,并提供了一套声明式的组件化的编程模型
- 本质是一个 JavaScript 库,已经帮助我们封装好的库
1.2 Vue3
- 2020 年 9 月 19 日,One piece 发布
- 具有更好的性能
- 更小的包体积
- 更好的ts集成
- 更优秀的 api 设计
1.3 使用 vue
- 安装和使用 vue 和 JavaScript 库
- 方式一:在页面中通过 CDN 的方式来引入
- 方式二:下载 Vue 的 JavaScript文件,自己手动导入
- 方式三:通过 npm 包管理工具安装使用它
- 方式四:直接通过 Vue CLI(脚手架)创建项目,并且使用它
1.4 CDN 引入
-
全局变量 Vue、函数 createApp、参数传入对象
-
在模板 template 里面编写 html
-
data:funcition(){return{titile:“hahh”}} 这个函数会返回一个新的对象
-
使用 const app 接收
-
这个对象使用挂载方法:参数为挂载到哪里:使用 #+id
-
底层是 querySelector
-
vue:将 template 里面的内容挂载到对应元素里面
-
本地引入 ctrl+a:全选 下载复制代码到 lib/vue.js
- script 引入
1.5 初体验
1.5.1 动态数据
- 在 createApp 的参数是一个对象,对象可以有多个属性:template、data(必须是一个函数,返回值是一个对象,对象里面可以包含属性)
- 差值语法:将某一个属性的值插入到 template 属性的内容里面来{{属性}}
- 开发启示:
- 以后开发先到服务器里面请求下来一些数据放到 data 函数里面的返回的对象里面
- 通过语法动态插入到模板里面,不需要手动操作 dom(声明式开发),而是交给 vue 自己操作dom
<div id="app"></div>
<script src="./lib/vue.js"></script>
<script>
const app = Vue.createApp({
template: `<h2>{{title}}</h2> <h1>{{message}}</h1>`,
data: function () {
return {
title: "hello world",
message: "你好呀这是美好的今天!"
}
}
})
app.mount("#app")
</script>
1.5.2 列表数据
- <li v-for=“item in movies”>{{item}}
<body>
<div id="app"></div>
<script src="./lib/vue.js"></script>
<script>
const app = Vue.createApp({
template: `
<h2>{{message}}</h2>
<h1>电影列表</h1>
<ul>
<li v-for="item in movies">{{item}}</li>
</ul>
`,
data: function () {
return {
message: "是一个优秀的孩子",
movies: ["aaaaaa", "bbbbbbb", "gggggggggg", "ttttttttttt", "ooooooooo"]
}
}
})
app.mount("#app")
</script>
</body>
1.5.3 计数器
- createApp方法里面的第三个属性:method
- methods 是一个对象:里面可以写很多方法,对 template 里面的元素进行操作
- 直接使用 this 进行操作,如 this.counter
- this 之所以能够找到那个对象是底层使用 bind 进行操作的
- 直接修改 data 中的数据
<body>
<div id="app"></div>
<script src="./lib/vue.js"></script>
<script>
const app = Vue.createApp({
template: `
<h1>当前计数:{{counter}}</h1>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
`,
data: function () {
return {
counter: 0
}
},
methods: {
increment() {
console.log("+1");
this.counter++
},
decrement() {
console.log("-1");
this.counter--
}
}
})
app.mount("#app")
</script>
</body>
1.6 声明式 vs 命令式
-
声明式关注 what to do:剩下的交给框架
- 声明一个模板
- 声明 data
- 声明 methods
- 不用管怎么实现的
- 会在createApp 传入的对象中声明需要的内容,模板 template、数据 data、方法 methods
- 这样的编写代码的过程,称之为是声明式编程
- 目前 Vue、React、Angular、小程序的编程模式,称之为声明式编程
-
命令式
- 命令式编程关注的是 “how to do”自己完成整个 how 的过程
- 每完成一个操作,都需要通过 JavaScript 编写一条代码,来给浏览器一个指令;
- 这样的编写代码的过程,称之为命令式编程
- 在早期的原生 JavaScript 和 jQuery 开发的过程中,都是通过这种命令式的方式在编写代码的
<body>
<h2>当前计数:<span class="counter"></span></h2>
<button class="add">+1</button>
<button class="sub">-1</button>
<script>
// 1. 获取dom
const h2El = document.querySelector("h2")
const counterEl = document.querySelector(".counter")
const addBtnEl = document.querySelector(".add")
const subBtnEl = document.querySelector(".sub")
// 2. 定义一个变量记录数据
let counter = 100
counterEl.textContent = counter
// 3. 监听按钮的点击
addBtnEl.onclick = function () {
counter++
counterEl.textContent = counter
}
subBtnEl.onclick = function () {
counter--
counterEl.textContent = counter
}
</script>
</body>
1.7 MVVM 模型
- 是一种软件的体系结构
- Model View ViewModel
- MVC在前端的应用
- html–》view
- javascript–》controller
- Vue 是一个 MVVM 的框架,但是官方说它并没有完全遵守 MVVM 的模型,但是整个设计是受到它的启发的
- view 里面写上特定的语法(eg:{{}},@click)
- vue 内部帮助你把这个数据绑定上去 data binding,vue 框架帮你绑定整个 dom listener
- Vue 相当于一个 ViewModel,在 view 视图和 Model 之间建立联系,来回进行数据沟通,沟通桥梁
- View:div
- Model:script
- Vue 的底层操作 dom
1.8 data属性
- vue2 的时候可以传入一个对象
- vue3 规定必须传入一个函数,该函数需要返回一个对象
- data 中返回的对象会被 Vue 的响应式系统劫持,被添加到 vue 的响应式系统中,之后对该对象的改修或者访问都会在劫持中被处理
- 之后数据的一举一动都会被监听
- vue2–》defineProperty
- vue3–》new Proxy
<div id="lili">{{message}}
<button @click="changeText">改变文字</button>
</div>
<script src="./lib/vue.js"></script>
<script>
const app=Vue.createApp({
data:function(){
return{
message:"hello lili"
}
},
methods:{
changeText:function(){
this.message="你好"
console.log("hahh");
}
}
})
app.mount("#lili")
</script>
1.9 methods 属性
- methods 里面的 this 必须是有值的,通过 this 获取到 data 返回对象中的数据
- 这个 this 不能是 window,在 window 中无法获取到 data 返回对象中的数据,使用箭头函数,这个 this 就是 window 了
- methods 中不能使用箭头函数定义函数,找到的是上层作用域的 this:全局作用域 window
- 里涉及到箭头函数使用 this 的查找规则,它会在自己的上层作用于中来查找 this
- 最终刚好找到的是 script 作用于中的 this,所以就是window
- this==》返回 proxy,vue 底层帮忙绑定了,所以它指向什么呢
- 对 methods 中的所有函数进行了遍历,并且通过 bind 绑定了 this
二、Vue基础-模板语法(一)
2.1 模板语法
-
App.vue
<template></template> <script></script>
-
react开发:
- react 使用的 jsx:编写的类似于js的一种语法
- 之后通过 Babel 将 jsx 编译成 React.createElement 函数调用
-
vue 开发
-
也支持 jsx 开发
-
基于 html 的模板语法
<h2 v-bind v-for v-once>dafsf{{}}</h2>
-
在 template 中允许开发者以声明式的方式将 dom 和底层组件实例的数据绑定在一起
-
底层中将模板编译成虚拟 dom 渲染函数
-
2.2 Mustache 语法
-
希望把数据显示到模板(template)中,使用最多的语法是 “Mustache”语法 (双大括号) 的文本插值
- data 返回的对象是有添加到Vue的响应式系统中
- 当 data 中的数据发生改变时,对应的内容也会发生更新
- Mustache 中不仅仅可以是 data 中的属性,也可以是一个 JavaScript 的表达式
-
差值语法、大胡子语法
-
基本使用 {{counter}}
-
表达式:{{counter*2}}
-
三元运算符
-
在里面不能定义语句
-
调用 methods 里面的函数
<body>
<div id="lili">
<!-- 1. 基本使用 -->
<h1> {{message}}</h1>
<h2>当前计数:{{counter}}</h2>
<!-- 2. 表达式 -->
<h2>双倍计数:{{counter*2}}</h2>
<h2>展示的信息:{{infos.split(" ")}}</h2>
<!-- 3. 三元运算符 -->
<h1>{{age>=18?"成年人":"小学生"}}</h1>
<!-- 4. 调用methods中的函数 -->
<h4>{{formatTime("yingyu")}}</h4>
<!-- 5. 不能定义语句 -->
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
counter: 0,
infos: "my name is lili",
age: 99
}
},
methods:{
formatTime(date){
return "2023-5-16"+date
}
}
})
app.mount("#lili")
</script>
</body>
2.3 v-once 指令
- 只会渲染一次,当数据发生变化时,元素或者组件以及其所有的子元素将视为静态内容并且跳过
- 子组件也不会重新渲染
- 用于性能优化
2.4 v-text 指令
- 会覆盖其他文本
<h2 v-text="message"></h2>
- vs mustache
2.5 v-html 指令
- 需要展示的内容包含 html,vue 是不会对其进行解析的
- data函数返回的对象属性中包含的内容:content:
html
标签等等 - 偶尔会用到
<h1 v-html="content"></h1>
2.6 v-pre 指令
- 不希望 vue 对{{}}进行解析
- 就是 {{}}
- v-pre 用于跳过元素和它的子元素的编译过程,显示原始的 Mustache 标签
- 跳过不需要编译的节点,加快编译的速度
2.7 v-cloak 指令
- clock:斗篷
- 加上斗篷让你慢慢渲染
- 因为有一些 vuejs 文件确实下载比较慢
- 可能先把大胡子展现出来了
- 需要结合 style 一起使用,将元素隐藏起来
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-clock] {
display: none;
}
</style>
</head>
<body>
<div id="lili">
<h1 v-cloak>{{message}}</h1>
</div>
<script src="../lib/vue.js"></script>
<script>
setTimeout(() => {
const app = Vue.createApp({
data: function () {
return {
message: "hello lili"
}
}
})
app.mount("lili")
}, 3000)
</script>
</body>
2.7 v-memo 指令
- 只更新绑定的值
<body>
<div id="lili" v-memo="[name]">
<p>{{message}}</p>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>身高:{{height}}</h2>
<button @click="changeInfo">改变信息</button>
</div>
<script src="../lib/vue.js"></script>
<script>
const app=Vue.createApp({
data:function(){
return{
message:"hello lili",
name:"lili",
age:99,
height:1000
}
},
methods:{
changeInfo(){
this.name = "kobe"
}
}
})
app.mount("#lili")
</script>
</body>
2.8 v-bind 绑定 属性
- 希望动态来绑定某些属性
- 绑定属性使用 v-bind
- 缩写: :
- 预期
- 参数
- 修饰符
- 用法:动态地绑定一个或多个 attribute,或向另一个组件传递 prop 值
- 语法糖:原来某种写法的简写
- v-bind 用于绑定一个或多个属性值,或者向另一个组件传递 props 值
<body>
<div id="lili">
<!-- 切换图片 -->
<!-- v-bind 语法糖:: -->
<button @click="switchImage"></button>
<!-- 1. 绑定img的src属性 -->
<!-- 错误写法 -->
<!-- <img src="{{imgURL}}" alt=""> -->
<img v-bind:src=" showImgURL" alt="">
<!-- 2. 绑定a的href -->
<a v-bind:href="href">淘宝</a>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
imgURL1: "http://baidu.com",
imgURL2: "http://baiduuu.com",
showImgURL: "http://baiduuu.com",
href: "http://taobao.com"
}
},
methods: {
switchImage: function () {
this.showImgURL = this.showImgURL === this.imgURL1 ? this.imgURL2 : this.imgURL1
}
}
})
app.mount("#lili")
</script>
</body>
2.9 绑定 class 全局属性
- class 是动态的
- 动态绑定 class 使用数组语法:
:class="[]"
- 数组里面也可以加对象
- 动态绑定 class 使用对象语法:
:class="{css名字:Boolean}"
- value 对应 data 中定义的变量
- 对象里面可以有多个属性
- 动态绑定的 class 可以和普通的 class 一起使用
<body>
<div id="lili">
<!-- 1. 基本绑定 -->
<h2 :class="classes">Hello lili</h2>
<!-- 2. 三元运算符 -->
<button :class="isActive? 'active' : ''" @click="btnClick">我是按钮yiyi</button>
<!-- 3. 使用对象语法 -->
<button :class="{active:isActive}" @click="btnClick">我是按钮erer</button>
<!-- 4. 对象语法有多个键值对 -->
<button :class="{active:isActive,lili:false,jiamiao:true}" @click="btnClick">我是按钮sansan</button>
<!-- 5. 动态绑定的class是可以和普通的class同时使用 -->
<button class="btn" :class="{active:isActive}">按钮sisi</button>
<!-- 6. 使用方法返回对象 -->
<button class="btn" :class="getDynamicClasses()" @click="btnClick">按钮wuwu
</button>
<!-- 7. 动态class可以写数组语法-->
<h1 :class="['abc','cba','kkk']">我是一个大标题</h1>
<!-- 8. 数组语法中可以写变量 -->
<h2 :class="['bbb',className]">我是一个中标题</h2>
<!-- 9. 数组中也可以使用对象 -->
<h3 :class="['ccc',{'active':isActive}]">我是一个小标题</h3>
<h3></h3>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
classes: "abc cba",
isActive: false,
className: 'nihao'
}
},
methods: {
btnClick() {
this.isActive = !this.isActive
},
getDynamicClasses() {
return { active: this.isActive }
}
}
})
app.mount("#lili")
</script>
</body>
2.10 绑定 style 属性
style=“{}”
- 后面跟对象类型
- 需要加引号表示一个整体或驼峰表示法
<body>
<div id="lili">
<!-- 1. 普通的html语法 -->
<h1 style="color:red">哈哈哈哈</h1>
<!-- 2. style中的某些值,来自于data中 -->
<h1 :style="{color:fontColor,fontSize:fontsize}">哈哈哈哈</h1>
<!-- 3. 动态板顶属性,该属性是一个对象 -->
<h2 :style="styleObj">haahhhhhh</h2>
<!-- 4. style的数组语法 -->
<h1 :style="[styleObj,{backgroundColor:'blue'}]">你好呀</h1>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
fontColor: "blue",
fontsize: "50px",
styleObj: {
color: "green"
}
}
}
})
app.mount("#lili")
</script>
</body>
2.11 动态绑定属性
- 属性名不确定,可以动态绑定个属性名
- class/style…不确定
- :[属性名]=“aaa”
<body>
<div id="lili">
<h2 :[title]="'aaaa'">蛋白爱人</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
title: "word"
}
}
})
app.mount("#lili")
</script>
</body>
2.12 绑定一个对象
-
希望绑定多个属性,v-bind=某一个对象
-
<h2 v-bind="infos"></h2>
-
infos:{hah:88},info 对象会被拆解成 div 的各个属性
-
经常用于给组件传递参数
<body>
<div id="lili">{{message}}
<!-- 一个一个地进行绑定 -->
<h1 :name="name" :age="age" :height="height">hello lili</h1>
<!-- 直接绑定对象:会自动遍历里面的所有属性,通常用于组件传值 -->
<h1 v-bind="props">props</h1>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
props: {
name: 'lili',
age: 12,
height: 1.99
},
name: 'lili',
age: 12,
height: 1.99
}
}
})
app.mount("#lili")
</script>
</body>
2.13 v-on 绑定事件
- 交互!!必须监听用户发生事件的监听,比如点击、拖拽、键盘事件
- 使用 v-on 指令监听事件,@click 是语法糖,简写
- 可以使用对象绑定多个事件
- 修饰符
- .stop - 调用 event.stopPropagation()
- .prevent - 调用 event.preventDefault()
- .capture - 添加事件侦听器时使用 capture 模式
- .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调
- .{keyAlias} - 仅当事件是从特定键触发时才触发回调
- .once - 只触发一次回调
- .left - 只当点击鼠标左键时触发
- .right - 只当点击鼠标右键时触发
- .middle - 只当点击鼠标中键时触发
- .passive - { passive: true } 模式添加侦听器 用法:绑定事件监听
<style>
.box {
width: 100px;
height: 100px;
background-color: orange;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="app">
<!-- 1.基本的写法 -->
<div class="box" v-on:click="divClick"></div>
<!-- 2.语法糖写法(重点掌握) -->
<div class="box" @click="divClick"></div>
<!-- 3.绑定的方法位置, 也可以写成一个表达式(不常用, 不推荐) -->
<h2>{{ counter }}</h2>
<button @click="increment">+1</button>
<!-- 行内表达式 -->
<button @click="counter++">+1</button>
<!-- 4.绑定其他方法(掌握) -->
<div class="box" @mousemove="divMousemove"></div>
<!-- 5.元素绑定多个事件(掌握) -->
<div class="box" @click="divClick" @mousemove="divMousemove"></div>
<!-- <div class="box" v-on="{ click: divClick, mousemove: divMousemove }"></div> -->
<!-- <div class="box" @="{ click: divClick, mousemove: divMousemove }"></div> -->
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
counter: 0
}
},
methods: {
divClick() {
console.log("divClick")
},
increment() {
this.counter++
},
divMousemove() {
console.log("divMousemove")
}
}
})
// 2.挂载app
app.mount("#app")
</script>
2.14 绑定事件的参数传递
-
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加
- 但是注意:如果方法本身中有一个参数,那么会默认将原生事件 event 参数传递进去
-
情况二:如果需要同时传入某个参数,同时需要 event 时,可以通过 $event 传入事件
-
默认参数:event 对象
-
绑定对象的时候没有任何的参数那么 event 对象会被默认传递进来
-
不能理解成函数调用,vue 会在发生事件的时候传递参数
<div id="app">
<!-- 1.默认传递event对象 -->
<button @click="btn1Click">按钮1</button>
<!-- 2.只有自己的参数 -->
<button @click="btn2Click('wtthy', age)">按钮2</button>
<!-- 3.自己的参数和event对象 -->
<!-- 在模板中想要明确的获取event对象: $event -->
<button @click="btn3Click('wtthy', age, $event)">按钮3</button>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1.创建app
const app = Vue.createApp({
// data: option api
data: function() {
return {
message: "Hello Vue",
age: 18
}
},
methods: {
// 1.默认参数: event对象
// 总结: 如果在绑定事件的时候, 没有传递任何的参数, 那么event对象会被默认传递进来
btn1Click(event) {
console.log("btn1Click:", event)
},
// 2.明确参数:
btn2Click(name, age) {
console.log("btn2Click:", name, age)
},
// 3.明确参数+event对象
btn3Click(name, age, event) {
console.log("btn3Click:", name, age, event)
}
}
})
// 2.挂载app
app.mount("#app")
</script>
2.15 v-on 修饰符
- 修饰符:对事件做了一些特殊的处理
- 阻止事件冒泡:@click.stop
- 阻止默认行为:.prevent
- 让事件传递变成捕获的模式:.capture
<body>
<div id="lili">
<div class="box" @click="divClick">
<button @click.stop="btnClick">button</button>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili"
}
},
methods: {
divClick() {
console.log("divClick");
},
btnClick() {
console.log("btnClick");
}
}
})
app.mount("#lili")
</script>
</body>
2.16 条件渲染
- 需要进行当前的条件来决定某些元素或组件是否渲染
- v-if v-else v-else-if v-show
- 使用实例
<body>
<div id="lili">
<!-- v-if = "条件" -->
<!-- 判断 对象 是否存在 -->
<div class="info" v-if="Object.keys(info).length ">
<h2>个人信息</h2>
<ul>
<!-- 之后使用v-for遍历对象 -->
<li>姓名:{{info.name}}</li>
<li>年龄:{{info.age}}</li>
</ul>
</div>
<!-- v-else -->
<div v-else>
<h2>没有信息哦</h2>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
info: {
name: "lili",
age: 12
}
}
}
})
app.mount("#lili")
</script>
</body>
<body>
<div id="lili">
<h1>你的分数:{{score}}</h1>
<h2 v-if="score > 90">优秀</h2>
<h2 v-else-if="score > 80">良好</h2>
<h2 v-else-if="score >= 60">及格</h2>
<h2 v-else>不及格</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
score: 9
}
}
})
app.mount("#lili")
</script>
</body>
- 指令
- v-if
- v-else
- v-else-if
- v-show:频繁切换 显示、隐藏
- v-if 是惰性的,当条件为 false 时,其判断的内容完全不会被渲染或者会直接被销毁
2.17 template 元素
-
从浏览器性能来说,多加一个元素需要多创建一个元素对象,原本的div没有存在的必要,从而提高了浏览器性能
-
vue3 才支持 template
- 目的就是为了把元素包裹在一起,不需要渲染 元素,作用就是为了在上面写指令
-
类似于小程序的 block
-
只是为了对指令做一个应用,与 v-if 结合使用
-
如果 div 没有实际的意义,可以用 template 进行代替
2.18 点击元素的显示和隐藏
- 通过控制变量进行修改
<body>
<div id="lili">
<div> <button @click="toggle">切换</button></div>
<template v-if="isShowCode">
<img src="https://game.gtimg.cn/images/yxzj/web201706/images/comm/floatwindow/wzry_qrcode.jpg" alt="">
</template>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
isShowCode: true
}
},
methods: {
toggle() {
this.isShowCode = !this.isShowCode
}
}
})
app.mount("#lili")
</script>
</body>
2.19 v-show 和 v-if 区别
2.19.1 v-show用法
- 不支持 template
- template 这个元素根本不存在,设置 display 没有用
- 不能和 v-else 等一起使用
2.19.2 本质区别
- v-show元素无论是否需要显示在浏览器上,它的DOM实际都是有存在的,只是通过 CSS 的display=none/block进行切换
- v-if 条件为 false 时,其对应的元素根本不会被渲染到 DOM 中
2.19.3 开发选择
- 元素需要在显示和隐藏之间频繁切换使用,使用 v-show
- 不会频繁地发生切换,那么使用 v-if
三、Vue基础-模板语法(二)
3.1 列表渲染
- 往往从服务器中获取的是一组数据,并且需要对其进行渲染
- 使用 v-for 进行渲染,类似于 js 中的 for 循环
3.2 v-for基本使用
- 基本格式 :v-for = “item in 数组”
- 数组通常来自 data 或者 prop
- 需要索引:(item,index)in 数组
- 遍历数组
- 遍历对象数组
- 在哪个标签写就遍历谁,谁就出现多个
<div idli>
<!-- 1. 电影列表进行渲染 -->
<h2>1. 电影列表</h2>
<ul>
<li v-for="(item,index) in movies">{{index+1}}-{{item}}</li>
</ul>
<!-- 2. 遍历数组复杂数据 -->
<h2>2. 商品列表</h2>
<div v-for="item in products" class="item">
<h3>商品:{{item.name}}</h3>
<span>价格:{{item.price}}</span>
<p>秒杀:{{item.desc}}</p>
</div>
</div>
3.3 v-for渲染类型
- 数组
- 对象
- 默认写 item in info:item 是 value
- (value,key,index)三个参数
- 字符串
- 数字:item in 100:从1~100
<body>
<div id="lili">
<!-- 1. 电影列表进行渲染 -->
<h2>1. 电影列表</h2>
<ul>
<li v-for="(item,index) in movies">{{index+1}}-{{item}}</li>
</ul>
<!-- 2. 遍历数组复杂数据 -->
<h2>2. 商品列表</h2>
<div v-for="item in products" class="item">
<h3>商品:{{item.name}}</h3>
<span>价格:{{item.price}}</span>
<p>秒杀:{{item.desc}}</p>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
// 1. movies
movies: ["aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg"],
// 2. 数组中存放对象
products: [
{ id: 110, name: "MacBook", price: 9.99, desc: "赶紧来买" },
{ id: 111, name: "note", price: 9.49, desc: "赶买" },
{ id: 310, name: "phone", price: 3.99, desc: "来买" },
{ id: 530, name: "Mac", price: 9.94, desc: "赶来买" },
{ id: 450, name: "Book", price: 1.99, desc: "赶紧" },
],
message: "hello lili"
}
}
})
app.mount("#lili")
</script>
</body>
<body>
<div id="lili">
<!-- 2. 遍历对象 -->
<ul>
<li v-for="(value,key,index) in info">{{value}}--{{key}}--{{index}}</li>
</ul>
<!-- 3. 遍历字符串 -->
<ul>
<li v-for="item in message">{{item}}</li>
</ul>
<!-- 4. 遍历数字 -->
<ul>
<li v-for="item in 100">{{item}}</li>
</ul>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
info: {
name: "lili",
age: 23,
height: 1.99
}
}
}
})
app.mount("#lili")
</script>
</body>
3.4 数组更新检测
- vue 将被侦听的数组的变更方法进行了包裹,所以它们也会触发视图更新
- 方法
- 创建一个新的数组
- 通过数组的方法,修改数组中的元素
- splice:删除
- push
- pop
- sort
- shift:删除第一个元素
- unshift:添加元素
- reverse
- 不修改原数组的方法是不能侦听的,该方法是返回一个新数组,需要对原数组进行赋值 filter()、concat() 和 slice();
- in===of
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
names: ["abc", "cba", "aaa", "bbb", "ccc"]
}
},
methods: {
changeArray() {
// 1. 直接将数组修改给一个新的数组
// this.names = ["why", "lili"]
// 2. 通过一些数组的方法,修改数组中的元素
// this.names.push("lili")
// this.names.pop()
// this.names.splice(2, 1, "hhh")
// this.names.sort()
// 3. 不修改原数组的方法不能监听(watch)
// this.names.map(item => item + "lili")
//需要重新赋值
const newInfos = this.names.map(item => item + "lili")
this.names = newInfos
}
}
})
app.mount("#lili")
</script>
3.5 v-for 的 key 属性
- 使用 v-for 进行列表渲染时候,vue 要求设置一个 key 属性
- key 要求是唯一的,一般用id来绑定
- :key=“”
- key 属性主要用在 vue 的虚拟 dom 算法,在新旧 nodes 对比时辨识 VNodes ,找到哪个东西不一样,没有必要全部都修改
- 比如在中间插入元素的时候,当没有设置 key 属性的时候,vue 修改的效率非常低,调用 patchUnkeyedChildren 方法,如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法,这样后面的都需要改了
- 他不知道后面的元素可以重复应用
- 如果后面绑定了 key,会识别到之前的节点和现在的节点是一样的,会进行复用
- 有 key :源码通过调用 patchKeyedChildren方法,尽可能的进行复用这个节点,而不是移动或者创建新的节点
- 使用时候加上key
3.6 认识VNode
- 全称是 virtual Node,虚拟节点
- 无论是组件还是元素,最终在Vue表现出来的都是一个个 VNode
- 本质是 js 对象
- 过程:template–》VNode(虚拟DOM)–》真实DOM
3.7 Vue 的虚拟 Dom
-
不只是一个简单的 div 而是有一大堆的元素,多个VNode应该会形成一个 VNode Tree
-
虚拟 dom 作用一:方便进行跨平台:通过虚拟dom根据不同渲染(浏览器、桌面端、移动端、VR设备)
- 目的:跨平台,写一份应用到不同地方
-
虚拟 dom 作用二:可以进行diff虚拟算法
- 只需要在中间插入fff即可
- 其他的节点都进行复用
四、Options API
4.1 复杂 data 的处理方式
-
在模板中可以直接通过插值语法显示一些 data 中的数据
-
需要对多个 data 数据进行运算、三元运算符来决定结果、多个数据进行转换、结合起来进行显示
-
在模板中放入太多的逻辑会让模板阅读性不强,模板过重,难以维护
-
在真实开发里面,某一些数据,需要一系列操作之后才能显示出来,并不是直接显示出来,需要进行一系列的运算
-
将逻辑抽离出去:
- method:只想要 fullname,却需要进行方法调用,加了小括号,看着不舒服
- computed 登场
4.2 computed 属性
- 对于任何包含响应式数据的复杂逻辑,都应该使用计算属性
- 在使用数据时候需要进行某种变换都需要使用计算属性
- 计算属性将被混入到组件实例中
- 所有 getter 和 setter 的 this 上下文自动地绑定为组件实例
- 计算属性更加见名知意
- 和 method 的区别
- computed 底层会缓存, 性能更高
- 计算属性会基于它们的依赖关系进行缓存;
- 在数据不发生变化时,计算属性是不需要重新计算的
- 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算
4.3 计算属性的缓存
- 基于他们的依赖关系进行缓存
- 当依赖的数据不发生变化的时候,计算属性是不需要重新计算的
- 如果依赖的数据发生变化,使用时,计算属性依然会重新计算,但是只刷新一次
4.4 计算属性的 set 和 get 写法
- Vue 源码内部只是做了一个逻辑判断而已
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
firstname: "lili",
lastname: "luelue"
}
},
computed: {
// 等同于
// 语法糖的写法
// fullname() {
// return this.firstname + " " + this.lastname
// },
// 完整写法
fullname: {
get: function () {
return this.firstname + " " + this.lastname
},
// 给计算属性设置值
set: function (value) {
const names = value.split(" ")
this.firstname = names[0]
this.middlename = names[1]
this.lastname = names[2]
}
}
},
methods: {
setFullname() {
this.fullname = "lili aiiii jiji"
}
}
})
app.mount("#lili")
</script>
4.5 侦听器 watch
- 希望在代码逻辑中监听数据的变化,需要用侦听器 watch 来完成了
- 当数据变化时,template 会自动进行更新来显示最新的数据
- 当 template 中发现数据经过一些变化之后,需要去执行一些逻辑(打印,发送网络请求)
- 监听 data/props 中的数据的变化:watch
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
info: { name: "ysy", age: 22 }
}
},
methods: {
changeMessage() {
this.message = "你好啊",
this.info = { name: "lili", age: 19 }
}
},
watch: {
// 1. 默认有两个参数:newValue/oldValue
message(newValue, oldValue) {
console.log("message数据发生变化", newValue, oldValue);
},
info(newValue, oldValue) {
// 2. 对象返回的是proxy
console.log("info数据发生变化",newValue,oldValue);
// 3. 获取原始对象
// 3.1 迭代方式:新的对象
console.log({...newValue});
// 3.2 Vue.toRaw:原来的值
console.log(Vue.toRaw(newValue));
}
}
})
app.mount("#lili")
</script>
4.6 method VS computed
- 对数据的展示上
- method
- 事实上先显示的是一个结果,但是都变成了一种方法的调用
- 多次使用方法的时候,没有缓存,也需要多次计算
- computed
- 使用的时候不需要加()
- 是有缓存的
- method
4.7 侦听器的配置选项
- 希望用户在 input 中输入一个问题
- 每当用户输入了最新的内容,就获取到最新的内容,并且使用该问题去服务器查询答案
- 需要实时的去获取最新的数据变化
- 例子
- 当点击按钮的时候会修改 info.name 的值
- 使用 watch 来侦听 info,不可以侦听
- 这是因为默认情况下,watch 只是在侦听 info 的引用变化,对于内部属性的变化是不会做出响应的
- 这个时候可以使用一个选项 deep 进行更深层的侦听
- watch 里面侦听的属性对应的也可以是一个 Object
- 当点击按钮的时候会修改 info.name 的值
- 还有另外一个属性,是希望一开始的就会立即执行一次: immediate 选项
- 这个时候无论后面数据是否有变化,侦听的函数都会有限执行一次
- 默认 watch 的监听是不会进行深度监听的
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
info: {
name: "why", age: 12
}
}
},
methods: {
changeInfo() {
// 1. 创建一个新对象,赋值给info
// this.info = { name: "lili" }
// 2. 直接修改原对象的某一个属性
this.info.name = "lili"
}
},
watch: {
// info(newValue, oldValue) {
// 默认watch的监听是不会进行深度监听的
// console.log("侦听到info改变", newValue, oldValue);
// }
// 进行深度监听
info: {
handler(newValue, oldValue) {
// 用的还是同一个对象,改变的只是属性
console.log("侦听到info改变", newValue, oldValue);
console.log(newValue === oldValue);
},
// 监听器选项
// info进行深度监听
deep: true,
// 第一次渲染直接执行一次监听器,oldValue为undefined
immediate: true
},
// 单独监听一个属性
'info.name': function (newValue, oldValue) {
console.log(newValue, oldValue);
}
}
})
app.mount("#lili")
</script>
4.8 侦听器 watch 的其他方式
<body>
<div id="lili">{{message}}
<button @click="changeMessage">修改message</button>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili"
}
},
methods: {
changeMessage() {
this.message = "你好呀"
}
},
// 生命周期回调函数:当前组件被创建时自动执行
// 一般在该函数中,会进行网络请求
created() {
// ajax/fetch/axios
this.$watch("message", (newValue, oldValue) => {
console.log("message变化", newValue, oldValue);
}, {
deep: true
})
}
})
app.mount("#lili")
</script>
</body>
4.9 购物车案例
<style>
table {
border-collapse: collapse;
}
th,
td {
padding: 8px 10px;
text-align: center;
border: 1px solid red;
}
.active {
background-color: aquamarine;
}
</style>
</head>
<body>
<div id="lili">
<template v-if="books.length">
<table>
<thead>
<tr>
<th>序号</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr :class="{active:index===curIndex}" @mouseover="mouseover(index)" v-for="(item,index) in books"
:key="item.id">
<td>{{index+1}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<td>${{item.price}}</td>
<td><button :disabled="item.count<=1" @click="decrease(index,item)">-</button>{{item.count}}<button
@click="increase(index)">+</button></td>
<td><button @click="removeBook(index)">移除</button></td>
</tr>
</tbody>
</table>
<h3>总价:${{price}}</h3>
</template>
<template v-else>
<h2>快买</h2>
</template>
</div>
<script src="../lib/vue.js"></script>
<script src="./data.js"></script>
<script>
const app = Vue.createApp({
data: function () {
return {
message: "hello lili",
books: books,
curIndex: -1
}
},
computed: {
price() {
let price = 0
for (const item of this.books) {
price += item.price * item.count
}
return price
},
},
methods: {
decrease(index, item) {
console.log("-", index);
let cur = this.books[index].count
if (cur > 1) this.books[index].count--
},
increase(index) {
console.log("-", index);
this.books[index].count++
},
removeBook(index) {
this.books.splice(index, 1)
},
mouseover(index) {
this.curIndex = index
}
}
})
app.mount("#lili")
</script>
</body>
五、Vue 基础-v-model 表单
5.1 v-model 的基本使用
- 表单提交
- 登录、注册时候需要提交账号密码
- 用户在检索、创建、更新时,需要提交一些数据
- 在代码逻辑中获取到用户提交的数据,通常会使用 v-model 指令来完成
- v-model 应用
- 在表单 input、textarea、textarea、select 元素上创建双向数据绑定
- 输入框一旦输入东西就会触发 v-on:input 事件
- 会根据控件类型自动选取正确的方法来更新元素
- 不过是一种语法糖
- 在表单 input、textarea、textarea、select 元素上创建双向数据绑定
- 原理
- v-bind 绑定 value 属性的值
- v-on 绑定 input 事件
<input v-model="searchText"/>
//等价于
<input :value="searchText" @input="searchText = $event.target.value"/>
<body>
<div id="lili">
<!-- 1. 手动实现双向绑定 -->
<!-- 用户名:<input type="text" :value="username" @input="userChange">
密码:<input type="password" :value="password" @input=" pwdChange">
<button>登录</button>
<h1>用户名:{{username}}</h1>
<h1>密码:{{password}}</h1> -->
<!-- 2. v-model实现双向绑定 -->
<!-- <input type="text" v-model="message">
<h1>message:{{message}}</h1> -->
<!-- 登录功能 -->
<label for="account">
账号:<input id="account" type="text" v-model="account" />
</label>
<label for="password">
密码:<input id="password" type="password" v-model="password" />
</label>
<button @click="loginClick">登录</button>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
// 每次绑定都得写属性
// username: "lili",
// password: '123456',
// message: "lilihhh"
account: "",
password: ""
}
},
methods: {
// 每次绑定都得写方法
userChange(event) {
this.username = event.target.value
},
loginClick() {
const account = this.account
const password = this.password
// url发送网络请求
console.log(account);
console.log(password);
}
}
})
app.mount("#lili")
</script>
</body>
5.2 v-model 绑定 textarea
<body>
<div id="lili">
<textarea cols="30" rows="10" v-model="content"></textarea>
<p>输入的内容:{{content}}</p>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
content: "hhhhhhhhh"
}
}
})
app.mount("#lili")
</script>
</body>
5.3 v-model 绑定 checkbox
- v-model 绑定 checkbox:单个勾选框和多个勾选框
- 单个勾选框
- v-model 即为布尔值
- 此时 input 的 value 属性并不影响 v-model 的值
- 多个复选框
- 当是多个复选框时,因为可以选中多个,所以对应的 data 中属性是一个数组
- 当选中某一个时,就会将 input 的 value 添加到数组中
<body>
<div id="lili">
<!-- 1. checkbox单选框:绑定到属性中的值是一个Boolean-->
<!-- value属性没有意义 -->
<label for="agree">
<input id="agree" type="checkbox" v-model="isAgree">同意协议
</label>
<h1>单选框:{{isAgree}}</h1>
<!-- 2. checkbox多选框:绑定到属性中的值是一个Array -->
<!-- 注意:在多选框当中,必须明确的绑定一个value值 -->
<!-- 当选中你的时候,会自动将value值放在属性当中 -->
<div class="hobbies">
<h1>请选择你的爱好</h1>
<label for="sing">
<input value="sing" type="checkbox" id="sing" v-model="hobbies">唱
</label>
<label for="jump">
<input value="jump" type="checkbox" id="jump" v-model="hobbies">跳
</label>
<h2>爱好:{{hobbies}}</h2>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
isAgree: false,
hobbies: []
}
}
})
app.mount("#lili")
</script>
</body>
5.4 v-model 绑定 radio
<div id="lili">
<div class="gender">
<label for="male">
<input id="male" type="radio" v-model="gender" value="男">男
</label>
<label for="female">
<input id="female" type="radio" v-model="gender" value="女">女
</label>
<h2>性别:{{gender}}</h2>
</div>
</div>
5.5 v-model 绑定 select
- select 也分单选和多选两种情况
- 单选:只能选中一个值
- v-model 绑定的是一个值
- 当选中 option 中的一个时,会将它对应的 value 赋值到 fruit 中
- 多选:可以选中多个值
- v-model 绑定的是一个数组
- 当选中多个值时,就会将选中的 option 对应的 value 添加到数组 fruit 中
- 单选:只能选中一个值
<body>
<div id="lili">
<!-- select的单选 -->
<select v-model="fruit">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<h2>单选:{{fruit}}</h2>
<hr>
<!-- select的多选 -->
<select multiple size="3" v-model="fruits">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<h2>多选:{{fruits}}</h2>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
fruit: "",
fruits: []
}
}
})
app.mount("#lili")
</script>
</body>
5.6 v-model 的值绑定
- 让用户选择的时候爱好是从服务器中拿到的,而不是手动写死的
- 值绑定:数据可能来自服务器,先将值请求下来,绑定到 data 返回的对象中,再通过 v-bind 来进行值的绑定
<body>
<div id="lili">
<!-- select的多选 -->
<select multiple size="3" v-model="fruits">
<option :value="item.value" v-for="item in allFruits" :key="item.value">{{item.text}}</option>
</select>
<h2>多选:{{fruits}}</h2>
<!-- checkbox值绑定 -->
<div class="hobbies">
<h1>请选择你的爱好</h1>
<template v-for="item in allHobbies" :key="item.value">
<label :for="item.value">
<input type="checkbox" :id="item.value" v-model="hobbies" :value="item.value">{{item.text}}
</label>
</template>
<h2>爱好:{{hobbies}}</h2>
</div>
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
allFruits: [
{ value: "apple", text: "苹果" },
{ value: "orange", text: "橘子" },
{ value: "banana", text: "香蕉" },
],
fruits: [],
// 爱好
allHobbies: [
{ value: "sing", text: "唱" },
{ value: "sing", text: "唱" },
{ value: "sing", text: "唱" },
],
hobbies: []
}
}
})
app.mount("#lili")
</script>
</body>
5.7 v-model 修饰符
-
lazy
- 默认情况下,v-model 在进行双向绑定的时候,绑定的是 input事件
- 加上 lazy 就将事件切换为 change事件,只有在提交时才会触发
-
number
- 自动将内容转换为数字
- 但建议使用 type = “number”
-
trim
- 去除首尾的空格
-
使用多个修饰符
<body>
<div id="lili">
<!-- 1. lazy:绑定change事件 -->
<input type="text" v-model.lazy="message">
<h1>message:{{message}}</h1>
<!-- 2. number:自动将内容转换为数字 -->
<input type="text" v-model.number="counter">
<h2>counter:{{counter}}--{{typeof counter}}</h2>
<input type="number" v-model="counter2">
<!-- 3. trim: 去除首尾的空格 -->
<input type="text" v-model.trim="content">
<h1>{{content}}</h1>
<!-- 4. 使用多个修饰符 -->
<input type="text" v-model.trim.lazy="info">
</div>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili",
counter: 3,
counter2: 2,
content: "哈哈哈哈或",
info: "厉害啊"
}
},
watch: {
content(newValue) {
console.log(newValue);
}
}
})
app.mount("#lili")
</script>
</body>
六、Vue 组件化基础-脚手架
6.1 人处理问题的方式
- 任何一个人处理信息的逻辑能力都是有限的
- 所以,当面对一个非常复杂的问题时,不太可能一次性搞定一大堆的内容
- 但是,人有一种天生的能力,就是将问题进行拆解
- 如果将一个复杂的问题,拆分成很多个可以处理的小问题,再将其放在整体当中,会发现大的问题也会迎刃而解
6.2 Vue 组件化开发思想
- 将一个页面中所以后的处理逻辑全部放在一起。处理起来会变得非常复杂,而不利于后续的管理以及扩展
- 将一个页面拆分成一个个小的功能块,每一个功能块完成术语自己这部分独立的功能,像搭积木一样组合在一起
- 三大框架、Flutter、移动端、小程序的开发
- 页面
- 拆分成哪些组件
- 每一个组件需要实现哪一些功能
- 每个组件又可以进行细分
- 组件本身又可以进行复用
- Vue.createApp({}) 里面传入的对象是一个根组件
- 组件树
- 相同逻辑的的东西抽取成一个组件
- 对象就是组件
6.3 注册 Vue 的全局组件
- 注册组件:
- 全局组件:在任何其他的组件中都可以使用的组件,任何地方都可以进行使用
- 局部组件:只有在注册的组件中才能使用的组件
<body>
<div id="lili">
<!-- 使用product-item组件 -->
<product-item></product-item>
<product-item></product-item>
<product-item></product-item>
</div>
<script src="../lib/vue.js"></script>
<script>
// 1. 组件:App组件(根组件)
const App = {
data() {
return {
title: "hello lili",
content: "我是内容哈哈哈哈"
}
}
}
// 2. 开发product-item组件
const productItem = {
template: `
<div class="product">
<h2>{{title}}</h2>
<p>{{content}}</p>
<div>商品图片</div>
<div>商品价格:<span>¥9.9</span></div>
<p>商品描述信息,9.9秒杀</p>
</div>
`
}//必须注册才能够使用
// 3. 创建app
const app = Vue.createApp(App)
// 4. 注册一个全局组件
app.component("product-item", productItem)
// 5. 挂载app
app.mount("#lili")
</script>
</body>
<body>
<div id="lili">
<product></product>
<product></product>
<product></product>
</div>
<template id="items">
<div class="item">
<h2>woshishangping</h2>
<h3>shangpingtupian</h3>
<h4>shangpingmiaoshuxinxi</h4>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp(App)
const App = {}
const productItem = {
template: "#items"
}
app.component("product", productItem)
app.mount("#lili")
</script>
</body>
6.4 注册全局组件
- app 中有一个方法,用来注册一个全局组件:
app.component("product-item",productItem)
// 组件名称 具体的对象- 组件(对象)里面是什么东西,product-item 里面就会渲染出什么东西
- 可以在任意的其他组件的 template 中使用
- 缺点:某些组件并没有用到,但是会被一起注册,类似于 webpack 这种打包工具在打包项目时,依然会对其进行打包,用户在下载对应的 js 会增加包的大小
<body>
<div id="lili">{{message}}
<home-nav></home-nav>
<product-item></product-item>
<product-item></product-item>
<product-item></product-item>
<product-item></product-item>
</div>
<template id="product">
<div class="product">
<div>商品图片</div>
<div>商品价格:<span>¥{{price}}</span></div>
<p>商品描述信息,9.9秒杀</p>
</div>
<button @click="favorItem">收藏</button>
</template>
<template id="nav">
<div class="nav">
<h2>navvvvvvvvvvvvvvvv</h2>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
const app = Vue.createApp({
data() {
return {
message: "hello lili"
}
}
})
// 注册全局组件
app.component("product-item", {
template: "#product",
data() {
return {
title: "我是商品的item",
price: 99
}
},
methods: {
favorItem() {
console.log("点击了收藏");
}
}
})
app.component("home-nav", {
template: "#nav"
})
app.mount("#lili")
</script>
</body>
6.5 组件的名称
- 在通过 app.component 注册一个组件的时候,第一个参数是组件的名称,定义组件名的方式有两种
- 短横线分割符 kebab-case
- 大驼峰标识符 PascalCase
- html 里面是不区分大小写的
- 后面写在 template 里面,是可以的
6.6 注册局部组件
- 通常采用局部注册
- 组件打算在哪里被使用(哪一个大组件中使用)
- 在根组件的 component 的 components 属性里面
- 局部组件的名称:{template data }
- 要想在局部组件里面使用局部组件,必须在局部组件中 components
- 该 components 选项对应的是一个对象,对象中的键值对是 组件的名称: 组件对象
<body>
<div id="lili">{{message}}
<product-item></product-item>
<home-nav>
</home-nav>
</div>
<template id="product">
<div class="product">
<div>商品图片</div>
<div>商品价格:<span>¥{{price}}</span></div>
<p>商品描述信息,9.9秒杀</p>
</div>
</template>
<template id="nav">
<div class="nav">
<h2>nnnnnnnnnnnnnnnnnnnavvvvvvvvvvvvvvvv</h2>
<product-item></product-item>
</div>
</template>
<script src="../lib/vue.js"></script>
<script>
const ProductItem = {
template: "#product",
data() {
return {
price: 99
}
}
}
const app = Vue.createApp({
data() {
return {
message: "hello lili"
}
},
// 告诉vue在这个组件里面会用到哪些别的组件
components: {
ProductItem
,
HomeNav: {
template: "#nav",
components: {
ProductItem
}
}
}
})
app.mount("#lili")
</script>
</body>
6.7 Vue 的开发模式
- 每个组件都会有自己的模板、脚本逻辑、样式
- script 在一个全局的作用域下,很容易出现命名冲突问题
- 代码为了适配一些浏览器,必须使用es5语法
- 编写完代码后,依然需要通过工具对代码进行建
- 解决方法:通过后缀名为 .vue 的单文件组织来解决,并且可以使用打包工具 webpack 或者 vite 或者 rollup 生成普普通通的js对象
6.8 如何支持 SFC
- single file component
- 使用
- 使用Vue CLI(脚手架)来创建代码环境,帮助我们做好了所有的配置选项,可以在其中直接使用 .vue 文件
- 自己使用 webpack 或 rollup 或 vite 这类打包工具
6.9 Vue CLI 脚手架
- 脚手架:帮助我们搭建项目的工具
- 在 vue 项目中使用的就是vue脚手架
- vue 的脚手架:Vue CLI
- 命令行界面
- 通过 CLI 选择项目的配置和创建出项目
- 已经内置了webpack 相关的配置,不需要从 0 开始配置
6.10 Vue CLI安装和使用
- 脚手架本身也是 npm 包,放在 npm registry 仓库里面
- npm i 脚手架名字 全局安装
- npm i @vue/cli -g
- 查看脚手架版本:vue --version
- 使用脚手架创建项目:vue create 01_product_demo
想支持哪些特性:
要不要生成预设
6.11 目录结构
6.12 jsconfig.json
- 给 VSCode 进行读取,给它更好的代码提示
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": ["src/*"],
"utils/*": ["src/utils/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
}
}
- 打包的时候从 main.js 文件开始
- 多层嵌套配置别名:vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
resolve: {
// 配置路径别名:@是已经配置好的路径别名,对应的是src路径
alias: {
'utils':"@/utils"
}
}
}
})
- 提示仍然不友好
6.13 引入的 vue 的版本
-
做法一: App.vue文件 SFC
- import {createApp} from ‘vue’
- 不支持 template 选项
- 有自己的作用域
- runtime 版本
- 使用 webpack 中的 vue-loader 帮助编译转化的过程,已经将 template 预先编译了
- import {createApp} from ‘vue’
-
做法二: 自己定义了一个对象:template/data/methods
- import {createApp} from ‘vue/dist/vue.esm-bunder.js’
- 由vue源码完成compile过程
- runtime + compile
- 标签元素compile==》createNode》Vnode》真实dom
- import {createApp} from ‘vue/dist/vue.esm-bunder.js’
6.14 Vue 文件 style 的作用域
- 不设置默认是全局属性,都会生效
- 设置 scoped,只针对于自己的作用域
6.15 Vue 项目的创建方式
-
Vue CLI:vue create
- webpack
-
npm init vue@latest
- 安装一个本地工具:create-vue
- 使用create-vue创建一个vue项目
- vite