组件化开发
深浅拷贝
- 对象是引用类型,会在 堆内存 中创建一块空间放对象相应内容
const info = { name: "why", age: 18 }
const obj = info
info.name = "kobe"
console.log(obj.name) // kobe
- 对象的浅拷贝
const info = { name: "why", age: 18, friend: { name: "kobe" } }
const obj = Object.assign({}, info)
info.friend.name = "james"
console.log(obj.friend.name) // james
- 对象深拷贝
const info = { name: "why", age: 18, friend: { name: "kobe" } }
const obj = JSON.parse(JSON.stringify(info))
info.friend.name = "james"
console.log(obj.friend.name) // kobe
v-model
表单提交是开发中非常常见的功能,也是和用户交互的重要手段:
- 比如用户在 登录、注册 时需要提交账号密码
- 比如用户在 检索、创建、更新 信息时,需要提交一些数据
我们可以在 代码逻辑中获取到用户提交的数据,我们通常会使用 v-model 指令来完成:
- v-model 指令可以在表单 input、textarea 以及 select 元素上创建 双向数据绑定
- 它会根据 控件类型 自动选取正确的方法来更新元素
- 尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件来更新数据,并在某种极端场景下进行一些特殊处理
v-model 原理
v-model 的原理其实是背后有两个操作:
- v-bind 绑定 value 属性的值
- v-on 绑定 input 事件监听到函数中,函数会获取最新的值赋值到绑定的属性中
v-model 更深层次的原理
v-model 绑定其他表单
v-model 还可以绑定其他的表单类型:textarea、checkbox、radio、select
v-model 绑定 textarea
<!-- 1.绑定textarea -->
<label for="intro">
自我介绍
<textarea name="intro" id="intro" cols="30" rows="10" v-model="intro"></textarea>
</label>
<h2>intro: {{intro}}</h2>
v-model 绑定 checkbox
- 单个勾选框:v-model 即为布尔值
- 多个勾选框:v-model 即为数组
<!-- 2.绑定checkbox -->
<!-- 2.1.单选框 -->
<label for="agree">
<input id="agree" type="checkbox" v-model="isAgree"> 同意协议
</label>
<h2>isAgree: {{isAgree}}</h2>
<!-- 2.2.多选框 -->
<span>你的爱好: </span>
<label for="basketball">
<input id="basketball" type="checkbox" v-model="hobbies" value="basketball"> 篮球
</label>
<label for="football">
<input id="football" type="checkbox" v-model="hobbies" value="football"> 足球
</label>
<label for="tennis">
<input id="tennis" type="checkbox" v-model="hobbies" value="tennis"> 网球
</label>
<h2>hobbies: {{hobbies}}</h2>
v-model 绑定 radio
<!-- 3.绑定radio -->
<span>你的爱好: </span>
<label for="male">
<input id="male" type="radio" v-model="gender" value="male">男
</label>
<label for="female">
<input id="female" type="radio" v-model="gender" value="female">女
</label>
<h2>gender: {{gender}}</h2>
v-model 绑定 select
- 单选:v-model 绑定的是一个值
- 多选:v-model 绑定的是一个数组
<!-- 4.绑定select -->
<span>喜欢的水果: </span>
<select v-model="fruit" multiple size="2">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<h2>fruit: {{fruit}}</h2>
v-model 修饰符
即使 type 设置为 number 最后查看的类型也是 string 类型
<input type="number" v-model="message">
<button @click="showType">查看类型</button>
逻辑判断时, 可以转化的情况下, 会隐式的自动将一个 string 类型成一个 number 类型再来进行判断(隐式转化)
const score = "100";
if (score > 90) {
console.log("优秀");
}
.lazy
:那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发.number
:希望转换为数字类型,那么可以使用 number 修饰符.trim
:自动过滤用户输入的收尾空白字符
<!-- 1.lazy修饰符 -->
<input type="text" v-model.lazy="message">
<!-- 2.number修饰符 -->
<input type="input" v-model.number="message">
<h2>{{message}}</h2>
<button @click="showType">查看类型</button>
<!-- 3.trim修饰符 -->
<input type="text" v-model.trim="message">
<button @click="showResult">查看结果</button>
组件化开发
组件化
- 如果我们将 一个页面中所有的处理逻辑全部放在一起,处理起来就会变得 非常复杂,而且不利于 后续的管理以及扩展
- 如果我们将 一个页面拆分成一个个小的模块,每个功能完成属于 自己这部分独立的功能,那么之后整个页面的 管理和维护 就变得非常容易了
我们需要通过组件化的思想来思考整个应用程序:
- 我们将一个 完整的页面 分成 很多个组件
- 每个组件 都用于实现 页面的一个功能块
- 而 每一个组件 又可以进行 细分
- 而 组件本身 又可以在 多个地方进行复用
- 组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
- 任何的应用都会被抽象成一颗组件树
注册组件方式
- 全局组件:在任何其他的组件中都可以使用的组件
- 局部组件:只有在注册的组件中才能使用的组件
我们先来学习一下全局组件的注册:
- 全局组件需要使用我们全局创建的 app 来注册组件
- 通过 component 方法传入组件名称、组件对象即可注册一个全局组件了
- 之后,我们可以在 App 组件的 template 中直接使用这个全局组件
<template id="my-app">
<component-a></component-a>
<component-b></component-b>
<component-c></component-c>
</template>
<template id="component-a">
<h2>{{title}}</h2>
<p>{{desc}}</p>
<button @click="btnClick">按钮点击</button>
</template>
<template id="component-c">
<h2>ComponentC</h2>
</template>
<script>
const App = {
template: "#my-app",
}
const app = Vue.createApp(App)
// 使用app注册一个全局组件app.component()
app.component("component-a", {
template: "#component-a",
data() {
return {
title: "我是标题",
desc: "我是内容, 哈哈哈哈哈",
};
},
methods: {
btnClick() {
console.log("按钮的点击");
},
},
})
app.component('ComponentC', {
template: "#component-c"
})
app.mount("#app");
</script>
组件的名称
方式一:使用 kabab-case(短横线分隔符)
当使用 kabab-case(短横线分割命名)定义一个组件时,你也必须在引用这个自定义元素时使用 kabab-case。例如:
<my-compoent-name>
方式二:使用 PascalCase(驼峰标识符)
当使用 PascalCase(首字母大写命名)定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。例如:
<my-compoent-name>
、<MyCompoentName>
在开发中我们只使用了 ComponentA、ComponentB,如果 ComponentC 没有用到但是我们依然在全局进行了注册,那么就意味着类似于 webpack 这种打包工具在打包我们的项目时,我们依然会对其进行打包。这样最终打包出的 JavaScript 包就会有关于 ComponentC 的内容,用户在下载对应的 JavaScript 时也会增加包的大小
- 局部注册是在我们需要使用到的组件中,通过 components 属性选项来进行注册
<template id="my-app">
<h2>{{message}}</h2>
<component-a></component-a>
</template>
<template id="component-a">
<h2>我是组件A</h2>
<p>我是内容, 哈哈哈哈</p>
</template>
<script>
const ComponentA = {
template: "#component-a"
}
const App = {
template: '#my-app',
components: {
// key: 组件名称 value: 组件对象
ComponentA: ComponentA
},
data() {
return {
message: "Hello World"
}
}
}
const app = Vue.createApp(App)
app.mount('#app')
</script>
Vue 的开发模式
目前我们使用 vue 的过程都是在 html 文件中,通过 template 编写自己的模板、脚本逻辑、样式等。但是随着项目越来越复杂,我们会采用组件化的方式来进行开发
- 这就意味着每个组件都会有自己的模板、脚本逻辑、样式等
- 当然我们依然可以把它们抽离到单独的 js、css 文件中,但是它们还是会分离开来
- 也包括我们的 script 是在一个全局的作用域下,很容易出现命名冲突的问题
- 并且我们的代码为了适配一些浏览器,必须使用 ES5 的语法
- 在我们编写代码完成之后,依然需要通过工具对代码进行构建、代码
所以在真实开发中,我们可以通过一个后缀名为 .vue 的 single-file components (单文件组件) 来解决,并且可以使用 webpack 或者 vite 或者 rollup 等构建工具来对其进行处理
单文件的特点:
- 代码的高亮
- ES6、CommonJS 的模块化能力
- 组件作用域的 CSS
- 可以使用预处理来构建更丰富的组件,比如 TypeScript、Babel、Less、Sass 等;
如何支持 SFC
- 方式一:使用 Vue CLI 来创建项目,项目会默认帮助我们配置好所有的配置选项,可以在其中直接使用 .vue 文件
- 方式二:自己 使用 webpack 或 rollup 或 vite 这类打包工具,对其进行打包处理