Vue学习笔记

组件化编程

组件

组件是实现页面的代码和资源的集合。组件以.vue结尾,在脚手架里面,不是说都放在components里,可复用的放在components里,不可复用的是项目特定的页面,放在views里。

传统方式编写应用,存在问题:

  • 依赖关系混乱,不好维护。
  • 代码复用率不高。

使用组件方式编写应用:

image-20230706144755365

组件的定义:

实现应用中局部功能代码和资源的集合。

组件一定要拆成局部,局部为一个组件,这样才能提高组件的复用率。其余项目要用,那么引入需要的组件就可以了。

模块

  1. 向外提供特定功能的js文件,一般一个模块就是一个js文件。

  2. 作用:

    复用js,简化js的编写,提高js运行效率。

非单文件组件

一个文件中包含有n个组件。

单文件组件是一个文件中只包含一个组件,文件名后缀为.vue。

真正做开发的时候,用的都是单文件组件,条理清晰,好维护。

vue里使用组件分三步:创建、注册、使用

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <!-- 引入vue -->
    <script type="text/javascript" src="../JS/vue.js"></script>
</head>

<body>
    <div id="root">
        <!-- 使用: 编写组件标签 -->
        <school></school>
        <hr>
        <student></student>
    </div>
</body>
<script type="text/javascript">
    Vue.config.productionTip = false

    //创建school组件
    const school = Vue.extend({
        template: `
        <div>
            <h2>学校名称: {{schoolName}}</h2>
            <h2>学校地址: {{address}}</h2>
        <div/>
        `,
        //不能写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        data() {
            return {
                schoolName: 'asdds',
                address: 'asddsa',
            }
        }
    })

    // 创建student组件
    const student = Vue.extend({
        template: `
        <div>
            <h2>学生姓名:{{studentName}}</h2>
            <h2>学生年龄:{{age}}</h2>
        <div/>
        `,
        //不能写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
        data() {
            return {
                studentName: 'asdds',
                age: 18,
            }
        }
    })

    // 创建vm
    new Vue({
        el: '#root',
        // 组件注册(局部注册)
        components: {
            school: school,
            student: student
        }
    })
</script>

</html>

Vue中使用组件的三大步骤:

  • 定义组件(创建组件)
  • 注册组件
  • 使用组件(写组件标签)

如何定义一个组件:

使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options一样,但也有区别:

  • el不写,因为所有定义的组件由Vue统一管理,由vm中的el决定服务哪个容器
  • data必须写成函数,避免组件被复用时,数据存在引用关系。
  • 备注:使用template可以配置组件结构。

如何注册组件:

  • 局部注册:new Vue()的时候传入components选项
  • 全局注册:Vue.component('组件名', 组件)

关于组件名:

  • 一个单词组成,首字母小写大写都可以
  • 多个单词组成,第一种写法my-school,第二种写法MySchool,这种写法需脚手架支持。

实例对象只有隐式原型属性,函数才有显式原型属性。

实例对象的隐式原型属性等于缔造者即函数的显式原型属性。

为什么要有这种内置关系:

让组件实例对象可以访问到Vue原型上的属性、方法。

单文件组件

直接暴露组件的配置对象

App.vue

<template>
  <div>
    <School></School>
  </div>
</template>

<script>
//引入组件
import School from "./School";
export default {
  name: "App",
  components: {
    School,
  },
};
</script>

<style>
</style>

School.vue

<template>
  <div class="demo">
    <h2>学校名称:{{ schoolName }}</h2>
    <h2>学校地址:{{ address }}</h2>
    <button @click="showName">点我提示学校名</button>
  </div>
</template>

<script>
export default {
  name: "School",
  data() {
    return {
      schoolName: "111",
      address: "beijingchangping",
    };
  },
  methods: {
    showName() {
      alert(this.schoolName);
    },
  },
};
</script>

<style>
.demo {
  background-color: orange;
}
</style>

入口文件

index.html

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<title>lianxi单文件组件语法</title>
</head>

<body>
	<div id="root">
		<App></App>
	</div>
	<script type="text/javascript" src="../JS/vue.js"></script>
	<script type="text/javascript" src="./main.js"></script>
</body>

</html>

main.js

import App from "./App.vue"

new Vue({
    el: '#root',
    components: { App }
})

Vue脚手架

初始化脚手架

说明:

Vue脚手架是Vue官方提供的标准化开发工具(开发平台)

具体步骤:

https://cn.vuejs.org/guide/quick-start.html

还可以

npm install -g @vue/cli (全局安装vue-cli工具,即命令行接口)

vue create project-name

这样就可以创建一个名为project-name的初始vue项目,脚手架,并且该脚手架已提供好了helloworld,可以通过npm run serve直接运行调试。

脚手架结构

node_modules/
public/
src/
.gitignore
index.html
babel.config.js
package.json
package-lock.json
README.md

.gitignore: git的忽略文件,哪些文件或文件夹不想接受git的管理,在这里配置。

babel.config.js:babel的控制文件,项目里肯定会涉及到ES6转ES5,怎么转换,采用什么样的标准,就是在这里面进行配置。一般来说直接用官方初始写好的就可以。

package.json:

只要打开的工程是一个符合npm规范的,就会有package.json,叫做包的说明书,里面有依赖库,包括一些调试命令。

  • serve:开发过程中,调试用的。
  • build:构建,所有的功能都写完了,想把整个工程变成浏览器认识的东西,就是编译,会生成一个dist目录。
  • lint:几乎不用,把vue文件和js文件都进行一次语法检查,因为vscode或者其他IDE一般都有语法检查。

package-lock.json:包版本控制文件,记录包的版本和下载地址等,锁死包的版本。不会下载包的时候,下载到不需要的版本。

src和public以该思路进行分析:从执行npm run serve之后,程序运行的入口开始。

src里有一个main.js,非常重要,执行npm run serve之后,程序直接就去找main.js,这是前端项目的入口。

main.js

/**
 * 该文件是整个项目的入口文件。
 */
// 引入vue
import Vue from 'vue'
// 引入App组件,它是所有组件的父组件
import App from './App.vue'
//关闭vue的生产提示。
Vue.config.productionTip = false

// 创建vue的实例对象vm
// new Vue({
//     // 下面这行代码将App组件放进了容器中
//   render: h => h(App),
// }).$mount('#app')

new Vue({
    el: '#app', //el用于指定当前vue实例为哪个容器服务,值通常为css选择器,这里的app是index.html中的容器
    // 下面这行代码是注册组件,将App组件放进了容器中
    render: h => h(App),
})

index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <!-- 针对IE浏览器的特殊配置,让IE以最高的渲染级别渲染页面 -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 开启移动端的理想视口 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!-- <%= BASE_URL %>指的就是public路径,避免项目部署到服务器上后产生各种路径问题,项目部署前需要build成dist目录 -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 配置网页标题,会去package.json中找-->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- 如果浏览器不支持js,noscript标签中的元素就会被渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <!-- 容器,main.js中的new的vue实例所绑定的容器-->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

为什么main.js是入口文件?这是脚手架配置好的。

为什么main.js都没有在index.html中引入,还能找到app这个容器呢?这也是脚手架配置好的。

render配置项

main.js最开始是这样写的:

new Vue({
    el: '#app',
    components: { App }
})

发现会报错:You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

我们通过这种ES6引入标准库的语法来引入的vue是一个残缺版的vue,即import Vue from 'vue'.

那么我们就不能用components这种写法来注册App,因为这个残缺版的vue不支持。我们只能用render函数来注册App父组件。

vue.js与vue.runtime.xxx.js的区别:

  • vue.js是完整版的vue,包含核心功能+模板解析器
  • vue.runtime.xxx.js是运行版的vue,只包含核心功能。

因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。

默认配置

vue脚手架默认隐藏了所有的webpack相关的配置,比如这些配置里决定了src目录下的main.js是入口文件,main.js中的vue实例需要为public下的index.html里的容器服务,这些都是默认配置好的,若想查看具体的webpack配置,需要执行:

vue inspect > output.js

vue.config.js

如果想个性化地定制脚手架,那么得使用这个配置文件,调整脚手架。这个文件脚手架里默认是没有生成的。

ref属性

  1. 这是标签的属性,被用来给元素或者子组件注册引用信息(id的替代者)

  2. 应用在html标签上获取的是真实dom元素,应用在组件标签上是组件实例对象(vc)

  3. 使用方式:

    <School ref="xxx"></School>
    获取:this.$refs.xxx
    

等到进行组件之间的通信时,这个ref属性就非常重要了。

配置项props

功能:让组件接收外部传过来的数据。

传递数据:

<Student name="zhangsan" sex="nan" :age="18"></Student>

接收数据,三种方式:

    // props:['name','sex','age']// 简单声明接收

    // 接收的同时,对数据进行类型限制
    // props:{
    //   name:String,
    //   age:Number,
    //   sex:String
    // }

    //先准备好props,随后在配置数据
    // 接收的同时,对数据进行类型限制,默认值的指定和必要性的限制
    props: {
      name: {
        type: String,
        required: true,
      },
      age: {
        type: Number,
        default: 99, //默认值,一般情况下默认值和required不会同时出现
      },
      sex: {
        type: String,
        required: true,
      },
    },

注意:props是只读的,vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务确实需要修改,那么请复制props的内容到data中一份,template也使用data中的数据,去修改data中的数据。

<template>
  <div>
    <h2>学生姓名:{{ name }}</h2>
    <h2>学生性别:{{ sex }}</h2>
    <h2>学生年龄:{{ myAge }}</h2>
    <button @click="updateAge"></button>
  </div>
</template>


<script>
  export default {
    name: "Student",
    data() {
      return {
        myAge: this.age, //props的优先级比data更高,所以可以这样写。获得了外部传进来的prop之后,再赋值给data,给template使用
      };
    },
    methods: {
      updateAge() {
        this.myAge++;
      },
    },
    // props:['name','sex','age']// 简单声明接收
    // 接收的同时,对数据进行类型限制
    // props:{
    //   name:String,
    //   age:Number,
    //   sex:String
    // }

    //先准备好props,随后在配置数据
    // 接收的同时,对数据进行类型限制,默认值的指定和必要性的限制
    props: {
      name: {
        type: String,
        required: true,
      },
      age: {
        type: Number,
        default: 99, //默认值,一般情况下默认值和required不会同时出现
      },
      sex: {
        type: String,
        required: true,
      },
    },
  };
</script>

<style>
</style>

配置项mixin(混入)

可以把多个组件公用的配置提取成一个混入对象,混入就是复用配置。

如果data中的数据、methods中的方法,混合里有,组件里也有,那么以组件的为主。

组件已经是在复用代码了,那组件内部如果还需要复用代码,可以借助混合去复用。

局部混入:

<script>
  // 引入一个混合
  import {mixin} from '../mixin'
  export default {
    name: "Student",
    data() {
      return {
        name: "zhangsan",
        age: 18
      };
    },
    mixins:[mixin]
  };
</script>

插件

vue里的插件本质上来说是一个对象。

插件的作用是用于增强Vue,本质是包含install方法的一个对象,install方法的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。

定义插件:

对象.install = function(Vue,options) {
	//1. 添加全局过滤器
    Vue.filter(...)
               
	//2. 添加全局指令
    Vue.directive(...)
    //3. 配置全局混入
    Vue.mixin(...)
    //4. 添加实例方法
    Vue.prototype.$myMethod = function() {...}
    Vue.prototype.$myProperty = xxx
}

使用插件:

在main.js里,

import plugins from './plugin.js'
Vue.use(plugins,1,2,3)

scoped样式

给style标签加上属性scoped,表示作用域,让样式局部生效,只作用在当前vue文件,防止冲突。

如果App.vue里写样式了,那么意味着这个样式是很多组件都需要用到的,一般在项目实际开发中不会这么写。

TodoList案例

组件化编码流程

拆分静态组件:抽取组件,使用组件实现静态页面效果。

展示动态数据:

  • 数据的类型、名称是什么?

  • 数据保存在哪个组件?--考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用?

    一个组件在用,放在组件自身即可;

    一些组件在用,放在他们共同的父组件上。

交互:从绑定事件监听开始。

注意

  1. props适用于:
    • 父组件 ==> 子组件 通信
    • 子组件 ==> 父组件 通信(要求父组件先给子组件传一个函数)
  2. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props的值是不能修改的。

浏览器本地存储(WebStorage)

localStorage最大的特点就是把浏览器关闭掉,也不会消失。

sessionStorage最大的特点就是把浏览器关闭掉,就清空了。

存储内容大小一般支持5MB左右,浏览器端通过Window.sessionStorage和Window.localStorage属性来实现本地存储机制。

相关API:

  • localStorage.setItem('key','value');
  • localStorage.getItem('key');
  • localStorage.removeItem('key');
  • localStorage.clear();

备注:

localStorage.getItem('key');

如果对应的value获取不到,那么getItem的返回值是null;

JSON.parse(null)的结果依然是null;

自定义事件

vue的自定义事件是区别于JS里内置事件而存在的。

<!--通过父组件给子组件传递函数类型的props实现:子给父传递数据-->
<School :getSchoolName="getSchoolName"/>
<!--通过父组件给子组件绑定一个自定义事件实现:子给父传递数据-->
<Student v-on:atguigu="getStudentName"/>

这是一种组件间通信的方式,适用于子组件==>父组件

绑定自定义事件:

  1. 第一种方式:

    在父组件中,<Demo @cy="test"/><Demo v-on:cy="test">

  2. 第二种方式

    <Demo ref="demo"/>
    ...
    mounted() {
    	this.$refs.demo.$on('cy', this.test);
    }
    

    test是自定义事件绑定的回调方法。

  3. 若想让自定义事件只触发一次,可以使用once修饰符,或$once方法。

  4. 触发自定义事件:

    this.$emit('cy',数据)

  5. 解绑自定义事件:

    this.$off('cy')

  6. 组件上也可以绑定原生DOM事件,需要使用.native修饰符

Last Updated:
Contributors: 陈杨