vue
vue的生命周期、vue实现双向数据绑定、Vue组件间的参数传递/ vue通信、vue路由、vue跨域、vuex、vue基础

beforeCreate(创建前) 在数据观测和初始化事件还未开始
created(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来
beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。
beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。
updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。
destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

1、什么是vue生命周期?

Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。

2、vue生命周期总共有几个阶段?/ vue生命周期具体的名字和过程?

它可以总共分为8个阶段:beforeCreate创建前、created创建后, beforeMount载入前、mounted载入后,beforeUpdate更新前、updated更新后,beforeDestroy销毁前、destroyed销毁后。

3、vue生命周期的作用是什么?

它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。

4、第一次页面加载会触发哪几个钩子?

会触发下面这几个beforeCreate, created, beforeMount, mounted 。

DOM 渲染在哪个周期中就已经完成?

DOM 渲染在 mounted 中就已经完成了。

简述每个周期具体适合哪些场景?

1、beforeCreate:可以在这加loading事件,在加载实例时触发。
2、created:初始化完成时的事件写在这里,如在这里结束loading,异步请求也适合在这里调用。
3、mounted:挂载元素,获取到dom节点。
4、updated:如果对数据统一处理,在这里写上相应的函数。
5、beforeDestroy:可以做一个确定停止事件的确认框。

聊聊你对Vue.js的template编译的理解?

先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)
1、通过compile编译器把template编译成AST语法树(abstract syntax tree 抽象语法树 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。
2、AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)


  • Vue实现数据双向绑定

1、Vue实现数据双向绑定的原理:Object.defineProperty()
2、vue实现数据双向绑定主要步骤:
(1)需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter。
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。
(2)compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
(3)Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
①在自身实例化时往属性订阅器(dep)里面添加自己
自身必须有一个update()方法
③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
(4)MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
3、js实现简单的双向绑定

<body>
    <div id="app">
    <input type="text" id="txt">
    <p id="show"></p>
</div>
</body>
<script type="text/javascript">
    var obj = {}
    Object.defineProperty(obj, 'txt', {
        get: function () {
            return obj
        },
        set: function (newValue) {
            document.getElementById('txt').value = newValue
            document.getElementById('show').innerHTML = newValue
        }
    })
    document.addEventListener('keyup', function (e) {
        obj.txt = e.target.value
    })
</script>

vue如何实现双向绑定/ vue双向绑定的原理/ vue双向绑定机制/ vue data是怎么实现的?

采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。


  • Vue组件间的参数传递、vue通信

7、父子组件、兄弟组件之间组件传值是怎么传的?

1、父组件与子组件传值

父组件传给子组件:子组件通过props方法接受数据;
子组件传给父组件:$emit 方法传递参数

2、非父子组件间的数据传递,兄弟组件传值eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。$emit 传值,$on() 接收值。项目比较小时,用这个比较合适。
3、整个状态存储:vuex


  • vue路由

1、Vue的路由实现:hash模式 和 history模式
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无害,hash不会重新加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。
Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

vue路由的实现/ vue路由的原理?

hash模式 和 history模式

路由之间跳转方式?

1、直接修改地址栏中的路由地址。
2、声明式(标签跳转):通过router-link实现跳转

<router-link to="/myRegister">注册</router-link>

3、编程式(js跳转):通过js的编程方式

this.$router.push('/myLogin');

怎么定义vue-router的动态路由以及如何获取传过来的动态参数?

1、定义路由:在router目录下的index.js文件中,对path属性加上/:id
2、获取参数:使用router对象的params.id

active-class是哪个组件的属性?

vue-router模块的router-link组件

嵌套路由怎么定义?

嵌套路由顾名思义就是路由的多层嵌套。一级路由里面使用children数组配置子路由,就是嵌套路由。

vue-router有哪几种导航钩子?它们有哪些参数?

3种。
1、全局导航钩子:

(1)前置守卫:跳转前进行拦截。
router.beforeEach(to, from, next)
(2)后置钩子
router.afterEach((to, from) => {})
2、组件内的钩子
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
3、单独路由独享组件。
beforeEnter: (to, from ,next)

const router = new VueRouter({
 routes: [
  {
   path: '/foo',
   component: Foo,
   beforeEnter: (to, from, next) => {
    // ...
   }
  }
 ]
})

vue-router导航钩子实现的原理?

待完善


  • vue跨域

vue实现跨域的方法有哪些?

1、proxyTable
这里以vue脚手架生成的标准项目为准。一般在项目config目录下面有个index文件。

里面格式如下:

'use strict'
const path = require('path')
module.exports = {
    dev: {
        // Paths
        assetsSubDirectory: 'static',
        assetsPublicPath: '/',
        proxyTable: {
            '/api': {
                target: 'http://localhost:7001',//后端接口地址
                changeOrigin: true, // 是否允许跨越
                pathRewrite: {
                    '^/api': '/api', // 重写,
                }
            }
        },
        host: '192.168.0.104',
        port: 8081,
        autoOpenBrowser: false,
        errorOverlay: true,
        notifyOnErrors: true,
        poll: false,
        useEslint: true,
        showEslintErrorsInOverlay: false,
        devtool: 'eval-source-map',
        cacheBusting: true,
        cssSourceMap: false,
    },

}

2、cors

CORS即跨源资源共享,它定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。但是CORS也具有一定的风险性,比如请求中只能说明来自于一个特定的域但不能验证是否可信,而且也容易被第三方入侵。 这里一般需要后端配合,开启cors。一般各种语言都有类似的包。比如NodeJS的koa2-cors

var koa = require('koa');
//npm install --save koa2-cors
var cors = require('koa2-cors');
var app = koa();
//开启
app.use(cors());

3、Nginx
4、后端程序代理

自己启一个后端程序做代理。然后把所有的请求转发到服务器。这里要用到node的一个包http-proxy-middleware。关键代码(express)如下

var proxy = require('http-proxy-middleware');
var app = express();
app.use('api', proxy({
	target: "http://baidu.com:7001",
	changeOrigin: true
}))

  • vuex

1、vuex可以理解为一种开发模式或框架。比如PHP有thinkphp,java有spring等。
通过状态(数据源)集中管理驱动组件的变化(好比spring的IOC容器对bean进行集中管理)。
2、应用级的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。

mutation 和action的区别

改变状态的方式,同步逻辑封装在mutation中,异步逻辑封装在action中。


  • vue基础

vue-cli怎样使用自定义的组件/ vue中引入组件的步骤?有遇到过哪些问题?

1、components目录创建组件文件,如indexPage.vue。
注意:script一定要export default{}
2、在需要的页面(组件)引入组件(2种方式):
(1)es6的import..from...
import indexPage from '@/components/indexPage.vue'
(2)CommonJS的require()
3、组件注册:注入到vue子组件的components属性上面,components{indexPage}
4、在template视图view中使用。
<index-page></index-page>
注意:命名时如果是indexPage,使用的时候用index-page

封装插件的过程?

使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。
待完善,vue.extend vue.components vue.use之类的,和封装组件之间的关系。

在vue中使用插件的步骤

1、引入插件(2种方式):
(1)es6的import...from...
(2)CommonJS的require()
2、使用全局方法Vue.use(plugin)使用插件,可以传入一个选项对象
Vue.use(myPlugin, {someOption: true})

methods, computed, watch 区别?

methods:简单处理
computed:数据在模板
watch:组件变化,做数据处理

vue自适应?

引入lib-flexible

emit、broadcast区别?

待完善

vue组件代码的健壮性?

待完善

vue的整个实现流程?

1、第一步:解析模板成render函数
template
2、第二步:响应式开始监听
object.defineProperty
data属性代理到vm上
3、第三步:首次渲染,显示页面,且绑定依赖
(1)为何要监听get,直接监听set不行吗?
①data中有很多属性,有些被用到,有些可能不被用到(data中没有人访问,就不会用get,如没有{{aaa}}指的就是aaa没有被访问)
②被用到的会走到get,不被用到的不会走到get
③未走到get中的属性,set的时候也无需关心
④避免不必要的重复渲染
4、第四步:data属性变化,触发rerender
defineProperty, get, set
(1)修改属性,被响应式的set监听到
(2)set中执行updateComponent
(3)updateComponent重新执行vm._render()
(4)生成的vnode和prevVnode,通过Patch进行对比
渲染到html

vue的优点是什么?

1、低耦合。视图(view)可以独立于model变化和修改,一个viewModel可以绑定到不同的”view“上,当view变化的时候,model可以不变,当model变化的时候view也可以不变。
2、可重用性。你可以把一些视图逻辑放在一个viewModel里面,让很多view重用这段视图逻辑。
3、独立开发。开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面设计。
4、可测试。界面素来是比较难测试的,而现在测试可以针对viewModel来写。

vue如何实现按需加载配合webpack配置?

1、webpack提供了require.ensure()来实现按需加载。以前引入路由是以import方式引入,后来改为const定义的方式引入。
2、不按需加载的引入方式:import home from '../../common/home.vue'
按需加载的引入方式:const home = r => require.ensure([], () => r(require('../../common/home.vue')))

说出至少4中vue中的指令和用法?

1、v-show指令是通过修改元素的display的css属性让其显示或者隐藏。
2、v-if:判断是否隐藏。
3、v-for:数据循环。
4、v-bind:class,绑定一个属性。
5、v-model:实现双向绑定。

v-show和v-if指令的共同点和不同点?

1、v-show指令是通过修改元素的display的css属性让其显示或者隐藏。
2、v-if指令是直接销毁和重建dom达到让元素显示和隐藏的效果。

如何让css只在当前组件中起作用?

将当前组件的

keep-alive的作用是什么?

<keep-alive></keep-alive包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。

什么时候用到vue.nextTick()?

1、created()钩子函数中,进行dom操作,要放在vue.nextTick()回调函数中。
原因:created()钩子函数执行的时候,dom其实并未进行任何渲染。
2、mounted钩子函数中,在数据变化中要执行某个操作,而这个操作需要随数据改变而改变数据结构。
原因:设置数据改变,vm.someData = ‘new value’, dom并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时,才会进行必要的dom更新。此时如果想要根据更新的dom状态去做某些事情时,就会出现问题。

为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) ,这样回调函数在 DOM 更新完成后就会调用。

vue-loader是什么?使用它的用途有哪些?

1、定义:vue-loader 是 webpack 的一个 loader,用于处理 .vue 文件,是解析.vue文件的一个加载器。
2、作用:js可以写成es6, style样式可以写scss、less, template中可以加jade。

scss是什么?在vue-cli中的安装使用步骤是什么?有哪几大特性?

1、css的预编译。
2、使用步骤:
(1)先装css-loader、node-loader、sass-loader等加载器模块。
(2)build目录找到webpack.base.config.js,extends属性中加一个扩展.scss
(3)在同一个文件,配置一个module属性。
(4)在组件的style标签上加上lang属性,如lang=“scss”
3、特性:
(1)可以使用变量,如($变量名称=值)。
(2)可以用混合器,混入@mixin可以传变量。
(3)可以嵌套。
(4)继承@extend不可以传变量,相同样式直接继承,不会造成代码冗余。基类未被继承时,也会被编译成css代码。

为什么使用key?

当有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。

为什么避免v-if和v-for一起使用?

1、当vue处理指令时,当v-for,v-if处于同一节点时,v-for比v-if具有更高的优先级。这意味着v-if将分别重复运行于每个v-for循环中。
2、解决:可以将v-if放在一个包装元素内。

<ul v-if="shouldRenderTodos">
	<li v-for="todo in todos">
		todo
	</li>
</ul>

请说出vue.cli项目中src目录每个文件夹和文件的用法?

assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件

自定义指令(v-check、v-focus)的方法有哪些?它有哪些钩子函数?它有哪些钩子函数参数?

1、定义指令的方法:
(1)全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。
(2)组件内定义指令:directives
2、钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)
3、钩子函数参数:el、binding

写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?

vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。
在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。
vue部分源码如下:

// vue项目  src/core/vdom/patch.js  -488行
// 以下是为了阅读性进行格式化后的代码

// oldCh 是一个旧虚拟节点数组
if (isUndef(oldKeyToIdx)) {
  oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
}
if(isDef(newStartVnode.key)) {
  // map 方式获取
  idxInOld = oldKeyToIdx[newStartVnode.key]
} else {
  // 遍历方式获取
  idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
}

创建map函数

function createKeyToOldIdx (children, beginIdx, endIdx) {
  let i, key
  const map = {}
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key
    if (isDef(key)) map[key] = i
  }
  return map
}

遍历寻找

// sameVnode 是对比新旧节点是否相同的函数
 function findIdxInOld (node, oldCh, start, end) {
    for (let i = start; i < end; i++) {
      const c = oldCh[i]
      
      if (isDef(c) && sameVnode(node, c)) return i
    }
  }
Logo

前往低代码交流专区

更多推荐