目录
3 v-slot: 插槽, 具名插槽 slot,slot-scope过时了 2.6.0使用v-slot
4 组件的data为什么是个函数然后返回对象,以前使用vm时都是个对象
5 scoped: style标签的scoped="scoped" 生成css的使用作用域只有当前组件内部的选择器生效
7 在使用计算属性的时候,函数名和 data 数据源中的数据可以 同名吗?
14、对于 Vue 中响应式数据原理的说法,下列哪项是不正确的?
组件一:
- <div id='app'>
- <Box>Box>
- div>
- <script>
- var vm = new Vue({
- el: '#app',
- data: {},
- methods: {},
- components: {
- Box: {
- template: `<div>
- <h1>6666h1>
- <p>hellop>
- div>`
- }
- },
- })
- script>
组件二:
- <div id='app'>
- <Box>Box>
- div>
- <script>
- let a1 = {
- template: `
-
6666
-
hello
- `
- }
- var vm = new Vue({
- el: '#app',
- data: {},
- methods: {},
- components: {
- //注册组件
- Box: a1
- }
- })
- script>
组件三:
- <div id='app'>
- <Box>Box>
- div>
- <script type="module">
- //1.导入
- import Box from "./componnets/Box.js"
- var vm = new Vue({
- el: '#app',
- data: {},
- methods: {},
- components: {
- Box
- }
- })
- script>
组件四:
- <div id='app'>
- <Box>Box>
- <Box2>Box2>
- div>
- <script type="module">
- //1.导入
- import Box from "./componnets/Box.js"
- var vm = new Vue({
- el: '#app',
- data: {},
- methods: {},
- components: {
- //2.注册组件
- Box,
- Box2:{
- template:`
-
666-box2
-
{{msg}}
- `,
- data:function(){
- return {msg:"msg66666"}
- },
- methods:{
- fn(){console.log("Box2组件的方法")}
- },
- filters: {},
- watch: {},
- computed: {},
- directives: {},
- beforeCreate() {},
- }
- },
- filters: {},
- watch: {},
- computed: {},
- directives: {},
- beforeCreate() {},
- })
- script>
全局:所有组件共同的功能(指令,过滤器,组件)
组件的属性不能用大写字母
注册组件时,组件名不要用系统已经使用过的,组件template模板中,只能有一个根元素
组件的名字可以用驼峰命名法,但是使用的时候必须用连字符
全局注册的组件使用时不能使用单标签(不会报错,但是只能使用一次 多次使用只显示第一个)注册的组件不要跟系统标签同名
局部:只有当前组件的功能(指令,过滤器,组件)
一个vm(根组件)实例可以有多个局部组件,但是只能供当前vm实例使用
.vue文件既可以是一个页面,也可以是一个组件
它可以被别人.vue文件引入 然后作为组件使用
- <style type="text/css">
- .app {
- width: 400px;
- height: 900px;
- background-color: gold;
- }
- .box1 {
- width: 200px;
- height: 200px;
- background-color: darkorange;
- margin: 20px;
- }
- .box11{
- width: 100px;
- height: 50px;
- background-color: hotpink;
- margin: 20px;
- }
- style>
- <div id='app' class="app">
- <Box1>Box1>
- <Box1>Box1>
- <Box2>Box2>
- <p>{{msg|box1tool}}p>
- <p v-color2="'blue'" v-color>{{msg|alltool1}}p>
- <allbox>allbox>
- div>
- <script>
- // 全局:所有组件共同的功能(指令,过滤器,组件)
- // 局部:只有当前组件的功能(指令,过滤器,组件)
- Vue.filter("alltool1",function(str){
- return str+"-alltool1"
- })
- Vue.filter("alltool2",function(str){ //注册过滤器
- return str+"-alltool1"
- })
- Vue.directive("color",function(el,obj){ //注册全局自定义指令
- el.style.color=obj.value||"red"
- })
- Vue.component("allbox",{
- template:"6666allbox"
- })
- var vm = new Vue({
- el: '#app',
- data: {
- msg:"appdata"
- },
- methods: {},
- components: {
- Box1: { // 不能访问上面的data
- template: `
-
{{msg|box1tool}}
-
{{msg|alltool1}}
-
-
- `,
- data: function() {
- return {
- msg: "hello"
- }
- },
- methods: {},
- filters: {
- box1tool(str) {
- return str + "box1-filter"
- }
- },
- directives:{
- color2:{
- inserted(el,obj){
- el.style.background=obj.value||"red"
- }
- }
- },
- components:{
- Box11:{ //组件的嵌套
- template:`
-
box11
- `
-
- }
- }
- },
- Box2:{
- template:`
-
{{msg|box1tool}}
-
{{msg|alltool1}}
- `,
- data:function(){
- return {
- msg:"box2data"
- }
- }
- }
- }
- })
- script>
vue create init ==>下载后启动,就会自己生成vue页面
引入:@1官方脚手架 @2挂载vm对象 @3组件引入并渲染到vm中
单文件组件也有全局组件和局部组件
只是把一个组件单独写在一个.vue文件中,供别的组件引入然后注册
引入文件时:一般使用相对路径 上一下用../ 同级使用./ 下级使用/ @ 代表src文件夹注册的组件名不能跟Vue中的和原生DOM的重名,注册的名字是驼峰,使用时就用连字符
注册的组件,使用时可以用双标签也可以用单标签:如果有插槽必须使用双标签
如果.Vue的script注释了,在打包的时候Vue的打包环境,会帮我们把这个文件解析为一个对象,然后给这个对象添加一个template属性,值为解析的页面模板
==>所以.Vue文件中可以没有script,style标签,但是不能没有template(注册组件的时候有就可以了,没有导出语法,加载器 脚手架会去创建对象)
(至少有一个模板或者render函数)
不写script标签==>称为静态组件
每一个组件内部只能有一个根元素,不要在根元素上写v-for
style标签可以写多个,也可以不写
在项目下创建vue.config.js 就是vue的打包配置文件:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false//关闭eslint的严格模式检测
})
- <div id="app">
- <nav1 son-proprety="子组件使用时属性传进去的值">nav1>
- <content1 img2src="./img/2.png">content1>
- div>
-
- <div id="app2">
- <nav1 son-proprety="子组件使用时属性传进去的值2">nav1>
- <content1 img2src="./img/2.png">content1>
- div>
- <script type="text/javascript">
- //祖册全局组件
- Vue.component("nav1",{
- data(){return {sondata:"子组件的数据"}},
- template:`
{{sondata}}
{{sonProprety}}
`, - props:["sonProprety"]
- })
-
-
- // let content1={
- // data(){return {img1:"./img/1.png"}},
- // template:`
![]()
`, - // props:["img2src"]
- // }
-
-
- new Vue({
- el:"#app",
- data:{},
- components:{
- // content1
- //注册局部组件
- content1:{
- data(){return {img1:"./img/1.png"}},
- template:`
![]()
`, - props:["img2src"]
- }
- }
- })
- new Vue({
- el:"#app2",
- data:{}
- })
- script>
组件一:
index.html
- <style type="text/css">
- .nav1{
- width: 100%;
- height: 40px;
- background-color: gainsboro;
- display: flex;
- justify-content: flex-start;
- flex-wrap: nowrap;
- }
- .nav1 div{
- margin-left: 20px;
- line-height: 40px;
- }
- .newsbox{
- width: 100%;
- padding: 20px;
- margin: 30px;
- background-color: paleturquoise;
- }
- style>
- <div id='app'>
- <Nav1>Nav1>
- <news-box v-for="el in 10">news-box>
- <Footer1>Footer1>
- div>
- <script type="module">
- import NewsBox from "./com/NewsBox.js"
- import Footer1 from "./com/Footer1.js"
- var vm = new Vue({
- el: '#app',
- data: {},
- methods: {},
- components:{
- Nav1:{
- template:`<div class="nav1">
- <div>btndiv>
- <div>btndiv>
- <div>btndiv>
- <div>btndiv>
- <div>btndiv>
- <div>btndiv>
- div>`
- },
- NewsBox:NewsBox,
- Footer1
- }
- })
- script>
com/NewsBox.js
- export default {
- template:`
-
新闻标题
-
新闻内容啊
- `,
- methods:{
- fn(){
- console.log(666666)
- }
- }
- }
com/Footer1.js
- export default {
- template:`footer`,
- mounted(){
- console.log("Footer1挂载到页面时就会运行")
- }
- }
组件二:
main.js
- import Vue from 'vue'
- import App from './App.vue'
-
- new Vue({
- render: h => h(App),
- }).$mount('#app')
-
App.vue
- <div>
- <Nav1/>
- <Nav1>Nav1>
- <Box1>Box1>
-
- <BoxDiv2>BoxDiv2>
-
- <hqyj>123123hqyj>
- div>
- template>
- <script>
- import Nav1 from "./Nav1.vue"
- // import Box1 from "./Box1.vue"
- //1.@代表了src目录的意思
- import Box1 from "@/Box1.vue"
- //2.注册的组件名不能跟vue中的和原生DOM的重名,注册的名字是驼峰 使用时就用连字符
- import BoxDiv2 from "@/com/BoxDiv2.vue"
- //3.注册的组件 使用时可以用双标签也可以用单标签: 如果有插槽必须用双标签
- export default {
- components: {
- Nav1,
- Box1:Box1,
- BoxDiv2,
- // boxdiv2
- }
- }
- script>
- <style>
- style>
属性有两种写法:简单声明和详细描述:
4.1.简单声明
props:["prop1","prop2"]4.2对属性做详细的描述
属性可以多传 但是注册了的属性就必须传 不然有可能会在使用时因为取值问题出BUG
属性名不要(不是不能)用vue官方或者原生标签已经签名过的名字:比如id class href
属性的类型验证只是一种验证提示 不会阻止程序运行
App.vue
- <div>
- <Nav1>Nav1>
- <box pro="title">box>
- <box :pro="title">box>
- <Goods :title1="arr[0].title" :price="arr[0].price">Goods>
- <Goods :title1="arr[1].title" :price="arr[1].price">Goods>
- <Goods title1="水煮肉片" price="12">Goods>
- <Goods2 :obj1="arr[0]">Goods2>
- <Goods2 v-for="el in arr" :obj1="el">Goods2>
- div>
- <script>
- import Nav1 from "@/components/Nav1.vue"
- import box from "@/components/box.vue"
- import Goods from "@/components/Goods.vue"
- import Goods2 from "@/components/Goods2.vue"
- export default {
- data() {
- return {
- msg: "hello",
- title:"app组件的数据",
- arr:[{title:"鱼香肉丝",price:18,id:12},
- {title:"鱼香肉丝2",price:181,id:122},
- {title:"鱼香肉丝3",price:183,id:123}
- ]
- }
- },
- components:{
- Nav1,
- box,
- Goods,
- Goods2
- }
- }
- script>
- <style>
-
- style>
box.vue
- <div>
- <h1>{{boxtitle}}h1>
-
- <h2>{{pro}}h2>
- div>
- <script>
- export default {
- //注册组件的属性
- props:["pro"],
- data() {
- return {
- boxtitle: "组件自己的数据"
- }
- },
- methods:{
- }
- }
- script>
- <style>
- style>
Godds2.vue
- <div>
- <p>{{obj1.title}}----------{{obj1.price}}p>
- div>
- <script>
- export default {
- props:["obj1"]
- }
- script>
- <style>
- style>
组件的属性的类型验证
如果不按照规定传,就会报警告,不会报错
- props: {
- propA: Number, // 基础的类型检查 (`null` 匹配任何类型)
- propB: [String, Number], // 多个可能的类型
- propC: { type: String,
- required: true // 必填的字符串
- },
- propD: { type: Number,
- default: 100 // 带有默认值的数字
- },
- propE: { type: Object, // 带有默认值的对象或者数组填Array
- default: function () { // 不建议直接填对象(因为对象直接量会一直占用内存),一般使用工厂函数,调用时才创建对象节省资源(面试)
- return { message: 'hello' }
- }
- },
- propF: {
- validator: function (value) {// 自定义验证函数返回为true就代表数据符合我们规定
- return ['success', 'warning', 'danger'].indexOf(value) !== -1
- }
- }
- }
例子:
App.vue
- <div>
- <Box :title="msg" :count="c">Box>
- <Box :title="msg" :count="c" color1="blue">Box>
- <Box :title="msg" :count="c" :pro1="pro1">Box>
- <Box :title="msg" :count="c" :pro1="pro2">Box>
- div>
- <script>
- import Box from "@/components/Box.vue"
- export default {
- data() {
- return {
- msg:"200",
- c:"hello",
- // c:["hello",100]
- pro1:"success",
- pro2:"test"
- }
- },
- components:{
- Box
- }
- }
- script>
- <style>
- style>
Box.vue
- <div>
-
- <h1>{{title}}h1>
- <p>{{count}}p>
- <div :style="{color:color1}">666div>
- <p>{{pro1}}p>
- div>
- <script>
- export default {
- //直接注册组件的属性
- // props:["title"]
- //配置的方式注册组件的属性
- props:{
- title:String,
- count:[Number,String],
- color1:{
- default:"red",
- type:String
- },
- pro1:{
- validator:function(v){
- var arr=["success","danger","info","parimary"]
- return arr.includes(v)
- }
- }
- },
- data() {
- return {
-
- }
- },
- }
- script>
- <style>
- style>
具名插槽 slot,slot-scope过时了 2.6.0使用v-slot
语法:v-slot:插槽名 语法糖:#插槽名
没有指定插槽名就是默认插入到插槽,不给插槽插入数据的话,就会使用组件的slot中的数据
插槽名不用使用引号引起来,直接写变量名
插入的内容必须是template标签或者组件 不能是原生的元素一般用在UI项目框架中
插槽的传值技术==>数据共享,共用
设计组件里面:(Box.vue)
默认槽位:< slot> < /slot>
具名槽位:< slot name="s1"> < /slot> < slot name="s2"> < /slot>
使用组件时: (APP.vue)
< 组件名> 尖括号中的东西插入默认槽位 < /组件名>
< 组件名>
< template v-slot:s2>插入东西必须放这个标签中,老版本不用< template>
< template #s1>插入东西必须放这个标签中,老版本不用< template>
< /组件名>
- //设计组件: .vue组件文件中
- <template>
- <div class="content1">
- <slot>slot>
- <slot name="slot1">slot>
- <h1>{{contentData.title}}h1>
- <h2>{{contentData.dt}}h2>
- <slot name="slot3">你不给我数据到3号插槽中 我就会默认显示出来slot>
- <p>{{contentData.text}}p>
- <slot name="slot2">slot>
- div>
- template>
- <script>
- export default{
- props:{
- contentData:{
- type:Object,
- default:()=>{return {title:"0",dt:"0",text:"0"}}
- }
- }
- }
- script>
- //使用组件: .vue页面文件中
- <template>
- <div>
- <content2 :contentData="arr[1]">
- <template #slot1>
- <img src="../assets/28.jpg">
- template>
-
- <template #slot2> //写法1
- <p>我在外部插入插槽的数据,不是子组件中的数据,也不是属性传进去的数据p>
- template>
-
- <template v-slot:slot3> //写法2
- 666
- template>
- <p>我并没有指定插入到哪里p>
- content2>
- div>
- template>
APP.vue
- <div class="app">
- <Box msg="mymsg" title="标题">
- <b>插槽的槽位b>
- Box>
- <Box msg="mymsg" title="标题"> 666 Box>
- <Box>
- <b>默认插槽的槽位b>
- <template v-slot:s1>
- <b>具名插槽的槽位b>
- template>
- Box>
- <Box>
- <b>默认插槽的槽位b>
- <template #s1>
- <b>2222b>
- template>
- Box>
-
- <Box>
- <b>默认插槽的槽位b>
- <Box2> Box2>
- <template #s1>
- <b>2222b>
- template>
- Box>
- div>
- <script>
- import Box from "@/components/Box.vue";
- import Box2 from "@/components/Box2.vue";
-
- export default {
- data() {
- return {};
- },
- components: {
- Box,
- Box2,
- },
- };
- script>
- <style>
- .app {
- width: 100%;
- min-height: 300px;
- background-color: honeydew;
- padding: 0px;
- border: 1px honeydew solid;
- }
- style>
Box.vue
- <div class="box">
- <div class="s">
- <slot>slot>
-
- div>
- <h1>{{ title }}h1>
- <div>{{ msg }}div>
- <slot name="s1">slot>
- div>
- <script>
- export default {
- props: ["msg", "title"],
- };
- script>
- <style scoped="scoped">
- .box {
- width: 100%;
- min-height: 100px;
- background-color: goldenrod;
- margin: 20px;
- }
- .s {
- width: 60px;
- height: 60px;
- background-color: ghostwhite;
- }
- style>
Box2.vue
- <i>box3i>
事件的三要素:事件源target 事件类型type 监听器handler
在组件内部触发事件 this.$emit("事件名",参数1,参数2.....)
使用组件时 绑定监听器,内部触发事件时 监听器就会调用
- //1.在原生组件(就是html标签)中 事件是由系统来设计触发条件的:
- "fn">点我
-
- //2.在自定义组件中,事件是由自己来设计什么时候触发的:
-
- //绑定事件:
- <mydiv @myclick="fn">点我mydiv>
-
- //事件设计:
- //在mydiv组件内部,你可以在你想要的条件下去触发事件
- this.$emit("myclick","要给触发的事件的函数传入值")//这个代码放在你想触发自定义事件的地方
-
-
- //3.如果希望组件绑定原生事件(事件的触发条件设计由系统设计)
- //给事件绑定事件修饰符 .native
native="fn">点我</mydiv>//事件名必须是系统存在的事件
APP.vue
- <div>
- <button @click="clicked">点击button>
-
- <Box v-on:myevent="fn">Box>
-
- <Box2 v-on:click="fn2">Box2>
-
- <Box3 v-on:click.native="fn3">Box3>
-
- div>
- <script>
- import Box from "./Box.vue";
- import Box2 from "./Box2.vue";
- import Box3 from "./Box3.vue";
- export default {
- methods: {
- clicked(e) {
- //事件的三要素: 事件源 target 事件类型type 监听器handler
- console.log("66666原生事件触发了", e);
- },
- fn() {
- //Box组件是事件源 myevent是Box组件绑定的事件类型 fn是Box组件上面绑定的监听器
- // fn要 myevent触发了就会运行 myevent事件什么条件下才会触发呢?
- //myevent由Box组件内部自己设计 什么时候触发:比如
- console.log("fn66666666666", arguments); //100,200
- },
- fn2() {
- console.log("fn26666666666");
- },
- fn3() {
- console.log("fn36666666666");
- },
- },
- components: {
- Box,
- Box2,
- Box3,
- },
- };
- script>
- <style scoped="scoped">
- style>
Box.vue
- <div>
- <button @click="add">增加button>
- <p>{{ count }}p>
- div>
- <script>
- export default {
- data() {
- return {
- count: 0,
- };
- },
- methods: {
- add() {
- this.count++;
- },
- },
- watch: {
- //绑定一个监听器,当count=5的时候,自己设定的那个事件就会触发
- count(v) {
- if (v == 5) {
- //触发组件内的事件
- this.$emit("myevent", 100, 200); //myevent触发的时候,就会给绑的那个事件fn传值
- }
- },
- },
- mounted() {
- //当挂载完之后,自己设定的那个事件就会触发
- this.$emit("myevent");
- },
- };
- script>
- <style>
- style>
Box2.vue
- <div>
- <button @click="x">box2button>
- div>
- <script>
- export default {
- methods:{
- x(){
- this.$emit("click")
- }
- }
- }
- script>
- <style>
- style>
Box3.vue
- <div>
- <button>box3button>
- div>
- <script>
- export default {};
- script>
- <style>
- style>
局部样式/CSS作用域的实现和原理
在style标签中添加一个scoped
原理就是vue的插件webpack打包时会:
把当前组件模板中的每一个元素添加一个相同的哈希值命名的属性名
并且为添加了scoped的style中的样式选择器上添加一个属性选择器
scoped: style标签的scoped="scoped" 生成css的使用作用域只有当前组件内部的选择器生效
vue文件中可以出现多个style标签 凡是添加了scoped属性的,里面的选择器就会被修改 没添加scoped属性的就不会被修改
app.vue
- <div>
- <h1 class="apptitle">app组件h1>
- <Box1>Box1>
- <Box2>Box2>
- div>
- <script>
- //谁先引入,谁先打包,那么后面引入的样式就会显示
- import Box2 from "@/components/Box2.vue"
- import Box1 from "@/components/Box1.vue"
- export default {
- components: {
- Box1,
- Box2
- }
- }
- script>
- <style>
- .apptitle{
- color: red;
- }
- style>
main.js
- import Vue from 'vue'
- import "./index.css"
- import App from './App.vue'
- new Vue({
- render: h => h(App),
- }).$mount('#app')
Box1.vue
- <div>
- <h1 class="title">box1页面h1>
- div>
- <script>
- export default {
- }
- script>
- <style scoped="scoped">
- .title{
- color: yellow;
- }
- style>
组件中的基础语法常见的面试题方向:
1 @ 是一个关键字,在引入的文件路径中 它代表src目录
2 template:组件的模板中只能有一个根节点
3 v-slot: 插槽, 具名插槽 slot,slot-scope过时了 2.6.0使用v-slot
语法:v-slot:插槽名 语法糖:#插槽名
没有指定插槽名就是默认插入到插槽,不给插槽插入数据的话,就会使用组件的slot标签的尖括号中的数据
插槽名不用使用引号引起来,直接写变量名
插入的内容必须是template标签或者组件 不能是原生的元素
4 组件的data为什么是个函数然后返回对象,以前使用vm时都是个对象
(组件是vue核心,组件最大的特点就是:复用)
数据源必须写成函数的形式,如果是对象的形式的话,那就是共用一个对象,就可以把数据源修改了。只有一个的数据源可以不是函数,就是跟组件(new Vue),因为它不会被复用
组件和挂载到界面的vm对象的区别,vm挂载到页面上时,触发了钩子函数的,data生成了,页面上使用的数据就是data容器中渲染上去的,而且页面只有一个vm对象,所以的vm生成完毕(mouted)时data必须存在
组件是引入和注册以后不一定使用的,比如for循环0次就是组件对象生成了的,但是使用0次,所以组件对象并没有使用自己的data容器去渲染数据,造成资源浪费,解决方案就是懒加载:当使用data时去调用,才生成data对象
组件的data 设计成function的用义:组件可以多次使用,每使用一次,函数被调用一次则创建出不同的数据对象,实现同名组件的数据可以相互独立
组件中的data是函数的设计是因为:
1、组件被复用时,data数据源才不会共用
2、函数的设计就像懒加载一样,当使用组件时,数据源的对象才会创建,这样设计性能更好
5 scoped: style标签的scoped="scoped" 生成css的使用作用域只有当前组件内部的选择器生效
6 数据劫持的顺序
本题是实际测试,答案背下一题的
this组件对象有很多属性和方法,都是劫持的“别人”的,比如data methods props,所以就有顺序,优先级的问题,写项目的时候要注意
this在构建时,给this设置成员的时间顺序:data>props属性>方法>计算属性>事件中给this添加的 (后劫持就会在页面中显示)
==>使用时的优先级:data
<事件中给this添加的 初始化顺序为 computed,methods,data,props ==> 实际并非如此 猜测是脚手架的导致的打包顺序还有vue的版本导致的劫持顺序不一样
如果eslint 配置比较严格的话,同名是编译不通过的。
7 在使用计算属性的时候,函数名和 data 数据源中的数据可以 同名吗?
可以同名,但 data 会覆盖 methods。并且本就不该同名,同名说明你命名不规范。覆盖的原因是 Props、methods、data、computed、watch 都是在 initState 函数中被初始化的。初始化顺序为 computed,methods,data,props (CMDP)本质上这些都是要挂载到 this 上面 的,你如果重名的话,后面出现的属性自然而然会覆盖之前挂载的属性了。
- Vue.prototype._init = function (options?: Object) {
- initLifecycle(vm) //1.初始化生命周期的函数
- initEvents(vm) //初始化事件
- initRender(vm)//生成vnode的模板
- callHook(vm, 'beforeCreate')//触发beforeCreate钩子
- initInjections(vm) //接受提供者提供的数据
- initState(vm)//初始化状态:具体过程在下面函数中
- initProvide(vm) //给别人提供数据
- callHook(vm, 'created')//触发created钩子
- }
-
- export function initState (vm: Component) {
- vm._watchers = []
- const opts = vm.$options
- if (opts.props) initProps(vm, opts.props) //有属性就初始化属性
- if (opts.methods) initMethods(vm, opts.methods)//有方法就初始化方法
- if (opts.data) {//有数据源就初始化数据源
- initData(vm)
- } else {
- observe(vm._data = {}, true /* asRootData */)//没有就监听自带的数据源
- }
- if (opts.computed) initComputed(vm, opts.computed)//有计算属性就初始化计算属性
- if (opts.watch && opts.watch !== nativeWatch) {
- initWatch(vm, opts.watch)//对属性监听
- }
- }
- //源码分析的结果:属性props>方法methods>数据源data>计算属性computed>对属性监听watch
8、怎么跑起来的项目
1、npm run dev 源代码 .vue文 webpack:vue集成的环境
2、vue文件,编程了一个对象,把模板解析了,sass,scoped等等
3、静态项目:html,css,js (8080托管)
==>用户浏览器 访问
render先执行,再去挂载$mount('#app')
9、如果el和$mount都写了,会不会冲突
不会,而且$mount的优先级更高
10、Vue加载流程:
1)每一个组件在加载时,都会调用vue内部的render函数来把这个组件的template选项的模板解析为一个JS对象
这个对象跟DOM节点对象“长得一摸一样”,就是为了后来的渲染
2.然后是数据劫持代理监听等等
底层有一种设计:发布/订阅设计 其实就是泄漏一个watcher函数去订阅(监听),数据的改变(底层JS语法就是defInedproty,v3是proxy)
当数据发生变化以后:
当状态(数据)变更的时候,重新构造一棵树的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异,把第二课树所记录的差异应用到第一颗树所构建的真正的DOM树上(patch),视图就更新了
旧VNode树=>树VNode树=>刷新页面==>错误想法
旧VNode树 和 生成的新VNode树 比较差异,然后更新DOM(刷新页面)==>正确思想,实现这个过程的代码就是DIFF算法
比较的过程是:一层一层的的比较,父组件和父组件,并不会把父组件跟子组件比较,同层级时,从两边到中间
比较的过程中,发现差异(组件/标签类型,文本,属性值,注释等等),就会异步去给DOM打补丁(操作页面)
11、什么是DIFF
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文 档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较(diff),记录两棵树差异
把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了
12、DIFF算法的过程 (了解)
当数据发生改变时,订阅者watcher就会调用patch给真实的DOM打补丁
通过sameVnode进行判断,相同则调用patchVnode方法
patchVnode做了以下操作:
找到对应的真实dom,称为el
如果都有都有文本节点且不相等,将el文本节点设置为Vnode的文本节点
如果oldVnode有子节点而VNode没有,则删除el子节点
如果oldVnode没有子节点而VNode有,则将VNode的子节点真实化后添加到el
如果两者都有子节点,则执行updateChildren函数比较子节点
updateChildren主要做了以下操作:
设置新旧VNode的头尾指针
新旧头尾指针进行比较,循环向中间靠拢,根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点,从哈希表寻找 key一致的VNode 节点再分情况操作
13、下列对 Vue 原理的叙述,哪些是正确的?
A. Vue 中的数组变更通知,通过拦截数组操作方法而实现
B. 编译器目标是创建渲染函数,渲染函数执行后将得到 VNode 树
C. 组件内 data 发生变化时会通知其对应 watcher,执行异步更新
D. patching 算法首先进行同层级比较,可能执行的操作是节点的增加、删除和更新选ABCD
14、对于 Vue 中响应式数据原理的说法,下列哪项是不正确的?
A. 采用数据劫持方式,即 Object.defineProperty() 劫持 data 中各属性,实现响应式数据
B. 视图中的变化会通过 watcher 更新 data 中的数据
C. 若 data 中某属性多次发生变化,watcher 仅会进入更新队列一次
D. 通过编译过程进行依赖收集选BD
15、 下列说法不正确的是哪项?
A. key 的作用主要是为了高效地更新虚拟 DOM
B. 若指定了组件的 template 选项,render 函数不会执行
C. 使用 vm.$nextTick 可以确保获得 DOM 异步更新的结果
D. 若没有 el 选项,vm.$mount(dom) 可将 Vue 实例挂载于指定元素上选B
16、面试考点
1、编译器的目的是为了调用render函数,返回VNode节点树
2、patch函数运行期间,是边比较边异步操作DOM
3、差异比较的顺序是 同层比较完毕,才会比较下层
4、同层差异比较是从两端到中间(具体的指针替换过程,自行查阅)
5、sameNode函数比较了哪些 :唯一key、元素名tag、数据data、注释isComment