Vue学习笔记
组件化编程
组件
组件是实现页面的代码和资源的集合。组件以.vue结尾,在脚手架里面,不是说都放在components里,可复用的放在components里,不可复用的是项目特定的页面,放在views里。
传统方式编写应用,存在问题:
- 依赖关系混乱,不好维护。
- 代码复用率不高。
使用组件方式编写应用:
组件的定义:
实现应用中局部
功能代码和资源的集合。
组件一定要拆成局部,局部为一个组件,这样才能提高组件的复用率。其余项目要用,那么引入需要的组件就可以了。
模块
向外提供特定功能的js文件,一般一个模块就是一个js文件。
作用:
复用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属性
这是标签的属性,被用来给元素或者子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实dom元素,应用在组件标签上是组件实例对象(vc)
使用方式:
<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案例
组件化编码流程
拆分静态组件:抽取组件,使用组件实现静态页面效果。
展示动态数据:
数据的类型、名称是什么?
数据保存在哪个组件?--考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用?
一个组件在用,放在组件自身即可;
一些组件在用,放在他们共同的父组件上。
交互:从绑定事件监听开始。
注意
- props适用于:
- 父组件 ==> 子组件 通信
- 子组件 ==> 父组件 通信(要求父组件先给子组件传一个函数)
- 使用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"/>
这是一种组件间通信的方式,适用于子组件==>父组件
绑定自定义事件:
第一种方式:
在父组件中,
<Demo @cy="test"/>
或<Demo v-on:cy="test">
第二种方式
<Demo ref="demo"/> ... mounted() { this.$refs.demo.$on('cy', this.test); }
test是自定义事件绑定的回调方法。
若想让自定义事件只触发一次,可以使用once修饰符,或$once方法。
触发自定义事件:
this.$emit('cy',数据)
解绑自定义事件:
this.$off('cy')
组件上也可以绑定原生DOM事件,需要使用.native修饰符