mvc和mvvm
-
mvc和mvvm
-
都是软件架构思想
-
mvc (react)
-
model: 数据保存
-
view: 用户界面
-
Controller: 业务逻辑
-
所有通信都是单向的. view -> controller -> model -> view
-
-
mvvm(vue)
-
model: 数据层
-
view: 视图层
-
viewmodel: 视图模型层
-
view和viewmodel是同步更改的,viewmodel再和model层进行通信
-
数据流向 都是单向
-
mvc: view -> controller -> model -> view
-
mvvm: view <-> viewmodel -><- model
-
-
-
vue中常见的指令
- v-for 循环
- v-if 判断是否成立,它不成立代码不加入
- v-show 是否显示,显示后display为block,如果后面不成立变为none
- v-bind 绑定变量 简写:
- v-on 事件 简写@
- v-else 如果v-if不成立,这个就成立
- v-else-if 再次判断
- v-model 绑定一个变量和当前表单的值绑定
- v-html 解析标签
- v-text 不解析标签
- v-once 只运行一次
- v-slot 插槽
- v-pre 不解析标签包裹中的{{变量}},这个变量不解析
- v-cloak 防止闪烁,在生命周期mounted前,变量在标签内无实际数据,这个可以防止在未有数据之前显示,而是有了数据后再显示出来
v-if和v-show的区别和场景,v-html和v-text的区别
v-if和v-show
区别:
1.手段:v-if是通过控制dom节点的存在与否来控制元素的显隐;v-show是通过设置DOM元素的display样式,block为显示,none为隐藏;
2.编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换;
3.编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载); v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留;
4.性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
场景:
如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好
v-html和v-text
v-text和{{}}表达式渲染数据,不解析标签。
v-html不仅可以渲染数据,而且可以解析标签。
vue生命周期
初始化事件,生命周期
beforeCreate
注入数据,初始化响应式数据
cteated
是否有el和template选项
beforeMount
将数据挂载到模板中,将虚拟DOM转成真实dom
mounted
数据改变时触发,dom的更新前后
beforeUpdate
updated
组件销毁的时候触发
beforeDestroy
destroyed
activated
缓存组件被激活时调用
deactivated
缓存组件被停用时调用
errorCaptured
子孙组件出错时调用
计算属性,方法,侦听器的区别
计算属性有缓存,方法无缓存,所以只有在数据发生变化的时候,计算属性才行重新执行
watch适合处理的场景是,侦听一个数的变化,当该数据变化,来处理其他与之相关数据的变化(该数据影响别的多个数据)
computed适合处理的场景是,获得一个值或者结果,该结果受其他的依赖的影响。(一个数据受多个数据影响)
描述一下插槽,过滤器和混入
Mixin混入:把各个组件下相同的js部分提取出来,分发给每一个用到的组件,这样可以减少代码量进行优化,但是分发到各个组件下的数据都是相互独立的 全局的mixin不需要在vue实例下进行mixins=[mixin] ,局部的要
//全局的mixin,不需要在实例vue里进行mixins=[mixin]
Vue.mixin({
methods: {
hand() {
console.log(1)
}
}
})
//局部
const mixin = {
data() {
return {
pageNum: 1,
pageCount: 10
}
},
created() {
console.log("mixin")
}
}
插槽:在子组件调用处的中间插入HTML的内容,叫做插槽
-具名插槽:在子组件分发插槽处,寻找父组件对应的插槽名字,把相应的HTML内容渲染到子组件中,注意HTML结构,如果不写名字,默认名字为default
-作用域插槽:子组件的数据传递给父组件时,使用作用域插槽
过滤:将传入的数据进行改变在输出
什么是前端路由,传统的路由实现?
-
一个url与相应的处理程序之间的映射关系
- 根据不同的url地址展示不同的内容或者页面
-
因为vue是个spa(单页面应用,只有一个html文件)
- 监听hashchange事件
vue-router
-
vue路由有哪几种模式,有什么区别
- 三种
- hash模式(hashchange事件)
- 他带#号,用起来比较好用,比较丑
- history模式(html5实现)
- 不带#号,比较好看,需要后端配合
- abstract
- 在 Weex 环境中使用
- hash模式(hashchange事件)
- 三种
-
什么是嵌套路由,动态路由和路由重定向
嵌套路由:如果一个页面内点击跳到下一个地址,但是页面没有进行跳转,那么这个跳转的路由写在当前路由下,作为child路由
动态路由:- 例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,我们可以在 vue-router 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果
- 动态路径参数 以冒号开头
path: ‘/detail/:id’
路由重定向:redirect
重定向也是通过 routes 配置来完成,可以从一个url重定向到另一个url
{ path: '/', // redirect是重定向 redirect: '/index', },
- 路由传参怎么做?- 通过动态路由做
- 描述一下路由守卫
全局前置守卫
const router = new VueRouter({ … })
router.beforeEach((to, from, next) => {
// …
})to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由全局后置钩子
router.afterEach((to, from) => {
// …
})路由独享守卫, 只有在这个路由里面做守卫
beforeEnter: (to, from, next) => {
// …
}
写在路由内部的组件内的守卫
写在.vue文件里面,当作option使用
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 this
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 this
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 this
} - 通过动态路由做
描述一下vuex(从下面几个方向回答)
-
是做什么的?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 -
有哪几个部分
-state- mutations
- actions
- getters
- module
-
每个部分都做什么
state
- 存放全局的状态的
- 获取: this.$store.state.xxx 这样也能修改,但是不推荐,因为无法事件记录变化,并且在严格模式下是无法这样修改的
- 修改: 通过mutations
- 辅助函数: …mapState(), 括号内可以写数组或者对象,写数组的话,名字无法改变,对象的话名字可以自己写;写在computed里面
mutations
- 写同步方法的,是唯一改变state的地方
- 书写: xxx(state) {} 可以直接调用state
- 调用: this.$store.commit(‘xxx’, payload), payload最好是传对象
- 辅助函数: …mapMutations(), 写在methods里面
actions
- 写异步方法,在异步方法里面调用mutations的方法
- 书写: xxx(context) {} context是一个对象,可以结构
- 调用: this.$store.dispatch(‘xxx’, payload)
- 辅助函数: …mapActions(), 写在methods里面
getters
- 相当于vuex里面的计算属性,逻辑复杂且多个组件都要用的时候
- 书写: xxx(state) { return …} 可以直接调用state
- 获取: this.$store.getters.xxx
- 辅助函数: …mapGetters(), 写在computed里面
module
- Vuex 允许我们用module将 store 分割成模块,模块化划分,便于管理
const moduleA = { state: () => ({ ... }), mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: () => ({ ... }), mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的状态 store.state.b // -> moduleB 的状态
- 获取state: this.$store.state.a.xxx
- 调用mutations: this.store.commit(‘xxx’) 方法是写在全局的,所以不需要写是哪个模块的,但是如果模块有命名,this.store.commit(‘b/xxx’)
-
状态的流向(流程图)
-
辅助函数
辅助函数: …mapState(), 括号内可以写数组或者对象,写数组的话,名字无法改变,对象的话名字可以自己写;写在computed里面
辅助函数: …mapMutations(), 写在methods里面
辅助函数: …mapActions(), 写在methods里面
辅助函数: …mapGetters(), 写在computed里面
组件传参怎么做
- 父子传参
- 非父子传参
- 父子组件之间的传参
-
父传子:
- 父组件用自定义的属性
- 子组件用props去接受 props: [‘msg’] this.msg
-
子传父
- 父组件用自定义事件,在父组件的template里写子组件 <child @message=“method”>
method (val) {
console.log(val)
} - 子组件用emit去发布 this.emit(‘message’, 传递的值),这个会自动激发父组件写的自定义事件
- 父组件用自定义事件,在父组件的template里写子组件 <child @message=“method”>
-
- 父子组件之间的传参
你常用的git命令有哪些
- init 初始化
- config 配置
git config --global user.name ‘用户名’
git config --global user.email ‘邮箱’ - add 提交到暂存区
- commit 提交到本地仓库
- push 上传到远程仓库
- pull 从远程仓库获取
- git log --graph --pretty=oneline 查看分支记录
- branch 查看分支 如果加个分支名就会创建一个分支 ; branch -a 查看远程分支
- fetch --all 获取远程分支名称到本地
- checkout 切换分支 ; checkout -b 分支名 这样就会创建并且切换
- merge 合并分支
- clone + 地址 克隆一个与远程仓库有关联的文件夹
- status 查看git的状态
- reset HEAD^ 回退一个版本
- git reset --hard 版本id 回退到指定版本
- remote add origin +地址 与远程仓库创建联系
- git diff 查看版本的差别
如果前端已经开发完成了,后端接口还没有给到,你是怎么做的
- 使用mock数据 采用fetchmock创建线上接口
TypeScript
typescript它不是一门新的语言,它是一门用来规范js的语言
js依旧是一门弱类型的语言
ts是js的一个超集,广义的js是es,dom,bom,ts是es的超集
比方说强类型,面对对象的开发体系
ts常用在大型项目中,使得代码更加规范,协作更加友好,维护更加方便
特点:
增加了静态类型检查,增加了代码的健壮性
是编写时报错,js是执行时报错
使得重构更加的安全与便捷
多人协作的时候,提高生产力,降低维护成本
常用的类型
string, number, boolean, any, void, never, Array<string> / string[], 枚举, 元组
string | number, interface
在vue里面使用ts
类组件,扩展组件, 函数组件
修饰器
服务端渲染
优势:
更好的SEO 搜索引擎优化
更快的到达时间
劣势:
更高的开发要求
更高的服务器的要求
nuxt.js
vue响应式原理
vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的getter,setter,当属性发生改变的时候,将变动发布给订阅者,触发监听回调
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化
vue3.0使用了proxy
浏览器的渲染原理和流程
- 浏览器将获取的HTML文档解析成DOM Tree(DOM树)。
- 将CSS样式表,解析成CSSOM Tree(CSS树)。
- 将DOM和CSSOM合并为渲染树(rendering tree),attachment
- 渲染树的每个元素经过精确的计算后,给出坐标,laylout。
- 将渲染树的各个节点绘制到屏幕上,painting。
为什么要使用虚拟DOM
- 虚拟DOM是为了解决浏览器性能问题而被设计出来的。
- 比如有10次循环,虚拟DOM不会立即操作DOM,而是将10次的diff内容保存在一个js对象里面,然后一次性的操作,避免大量无谓的计算
- 操作内存中的js对象的速度显然更快,等更新完后,再将最终的js对象映射成真实的DOM,交由浏览器绘制。
diff操作
数据改变,虚拟DOM也会改变
我们不想重新渲染vdom,我们只想渲染改变的部分,就需要用到diff算法
两个树如果完全比较,时间复杂度是O(n^3),Vue的Diff算法的时间复杂度是O(n),意味着只能平层的比较两棵树的节点,放弃了深度遍历。似乎牺牲了一定的精准性来换取速度,但考虑到前端页面通常不会跨层移动DOM元素,这样做是优先的
采用了同级比较,只有count相同的children进行比较
比较他们的key,进行增加,删除,替换,换位等操作
自定义指令
全局定义:他也可以加修饰符,绑定元素的所有内容,全部在binding里,可以通过判断修饰符的内容进行一些操作
//写法一
Vue.directive(‘focus’, { //定义一个v-focus
// 当被绑定的元素插入到 DOM 中时……
inserted: function(el, binding) { //el指的是绑定的元素
// 聚焦元素
el.focus()
}
})
//全局的第二种写法
Vue.directive('focus', {
bind() {},//bind表示当前指令绑定到该元素身上,立刻触发
// inserted表示绑定的元素已经渲染为真实的dom,并且在页面中已经可以看到
inserted: function(el) {
// 聚焦元素
el.focus()
}
})
局部定义,只能在当前组件使用
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}