Skip to content

组件化开发

深浅拷贝

  • 对象是引用类型,会在 堆内存 中创建一块空间放对象相应内容
js
const info = { name: "why", age: 18 }
const obj = info
info.name = "kobe"
console.log(obj.name) // kobe
  • 对象的浅拷贝
js
const info = { name: "why", age: 18, friend: { name: "kobe" } }
const obj = Object.assign({}, info)
info.friend.name = "james"
console.log(obj.friend.name) // james
  • 对象深拷贝
js
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 事件监听到函数中,函数会获取最新的值赋值到绑定的属性中

image-20220627175403301

v-model 更深层次的原理

image-20220628100157618

v-model 绑定其他表单

v-model 还可以绑定其他的表单类型:textarea、checkbox、radio、select

v-model 绑定 textarea

html
<!-- 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 即为数组
html
<!-- 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

html
<!-- 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 绑定的是一个数组
html
<!-- 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 类型

html
<input type="number" v-model="message">
<button @click="showType">查看类型</button>

逻辑判断时, 可以转化的情况下, 会隐式的自动将一个 string 类型成一个 number 类型再来进行判断(隐式转化

js
const score = "100";
if (score > 90) {
  console.log("优秀");
}
  • .lazy:那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发
  • .number:希望转换为数字类型,那么可以使用 number 修饰符
  • .trim:自动过滤用户输入的收尾空白字符
html
<!-- 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>

组件化开发

组件化

  • 如果我们将 一个页面中所有的处理逻辑全部放在一起,处理起来就会变得 非常复杂,而且不利于 后续的管理以及扩展
  • 如果我们将 一个页面拆分成一个个小的模块,每个功能完成属于 自己这部分独立的功能,那么之后整个页面的 管理和维护 就变得非常容易了

image-20220628151403878

我们需要通过组件化的思想来思考整个应用程序:

  • 我们将一个 完整的页面 分成 很多个组件
  • 每个组件 都用于实现 页面的一个功能块
  • 每一个组件 又可以进行 细分
  • 组件本身 又可以在 多个地方进行复用

image-20220628155437322

  • 组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
  • 任何的应用都会被抽象成一颗组件树

注册组件方式

  • 全局组件:在任何其他的组件中都可以使用的组件
  • 局部组件:只有在注册的组件中才能使用的组件

我们先来学习一下全局组件的注册:

  • 全局组件需要使用我们全局创建的 app 来注册组件
  • 通过 component 方法传入组件名称、组件对象即可注册一个全局组件了
  • 之后,我们可以在 App 组件的 template 中直接使用这个全局组件
html
<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 属性选项来进行注册
html
<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 这类打包工具,对其进行打包处理

常备不懈,才能有备无患