angular
的特点,在数据操作方面更为简单;react
的优点,实现了 html
的封装和重用,在构建单页面应用方面有着独特的优势;dom
操作是非常耗费性能的, 不再使用原生的 dom
操作节点,极大解放 dom
操作,但具体操作的还是 dom
不过是换了另一种方式;react
而言,同样是操作虚拟 dom
,就性能而言, vue
存在很大的优势。答:因为 JavaScript
的特性所导致,在 component
中,data
必须以函数的形式存在,不可以是对象。组建中的 data
写成一个函数,数据以函数返回值的形式定义,这样每次复用组件的时候,都会返回一份新的 data
,相当于每个组件实例都有自己私有的数据空间,它们只负责各自维护的数据,不会造成混乱。而单纯的写成对象形式,就是所有的组件实例共用了一个 data
,这样改一个全都改了。
答:主张最少;可以根据不同的需求选择不同的层级;
作者:徐飞
链接:https://www.zhihu.com/question/51907207/answer/136559185
在我看来,渐进式代表的含义是:主张最少。每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:- 必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。比如React,它也有一定程度的主张,它的主张主要是函数式编程的理念,比如说,你需要知道什么是副作用,什么是纯函数,如何隔离副作用。它的侵入性看似没有Angular那么强,主要因为它是软性侵入。你当然可以只用React的视图层,但几乎没有人这么用,为什么呢,因为你用了它,就会觉得其他东西都很别扭,于是你要引入Flux,Redux,Mobx之中的一个,于是你除了Redux,还要看saga,于是你要纠结业务开发过程中每个东西有没有副作用,纯不纯,甚至你连这个都可能不能忍:const getData = () => { // 如果不存在,就在缓存中创建一个并返回 // 如果存在,就从缓存中拿}因为你要纠结它有外部依赖,同样是不加参数调用,连续两次的结果是不一样的,于是不纯。为什么我一直不认同在中后台项目中使用React,原因就在这里,我反对的是整个业务应用的函数式倾向,很多人都是看到有很多好用的React组件,就会倾向于把它引入,然后,你知道怎么把自己的业务映射到函数式的那套理念上吗?函数式编程,无副作用,写出来的代码没有bug,这是真理没错,但是有两个问题需要考虑:1. JS本身,有太多特性与纯函数式的主张不适配,这一点,题叶能说得更多2. 业务系统里面的实体关系,如何组织业务逻辑,几十年来积累了无数的基于设计模式的场景经验,有太多的东西可以模仿,但是,没有人给你总结那么多如何把你的厚重业务映射到函数式理念的经验,这个地方很考验综合水平的,真的每个人都有能力去做这种映射吗?函数式编程无bug的根本就在于要把业务逻辑完全都依照这套理念搞好,你看看自己公司做中后台的员工,他们熟悉的是什么?是基于传统OO设计模式的这套东西,他们以为拿着你们给的组件库就得到了一切,但是可能还要被灌输函数式编程的一整套东西,而且又没人告诉他们在业务场景下,如何规划业务模型、组织代码,还要求快速开发,怎么能快起来?所以我真是心疼这些人,他们要的只是组件库,却不得不把业务逻辑的思考方式也作转换,这个事情没有一两年时间洗脑,根本洗不到能开发业务的程度。没有好组件库的时候,大家痛点在视图层,有了基于React的组件化,把原先没那么痛的业务逻辑部分搞得也痛起来了,原先大家按照设计模式教的东西,照猫画虎还能继续开发了,学了一套新理念之后,都不知道怎么写代码了,怎么写都怀疑自己不对,可怕。我宁可支持Angular也不支持React的原因也就在此,Angular至少在业务逻辑这块没有软主张,能够跟OO设计模式那套东西配合得很好。我面对过很多商务场景,都是前端很厚重的东西,不仅仅是管理控制台这种,这类东西里面,业务逻辑的占比要比视图大挺多的,如何组织这些东西,目前几个主流技术栈都没有解决方案,要靠业务架构师去摆平。如果你的场景不是这么厚重的,只是简单管理控制台,那当我没说好了。框架是不能解决业务问题的,只能作为工具,放在合适的人手里,合适的场景下。现在我要说说为什么我这么支持Vue了,没什么,可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。你可以在底层数据逻辑的地方用OO和设计模式的那套理念,也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。渐进式的含义,我的理解是:没有多做职责之外的事。
cli2 版本: 将 config/index.js
里的 assetsPublicPath
的值改为 ./
。
build: {
// ...
assetsPublicPath: ./ ,
// ...
}
cli3版本: 在根目录下新建vue.config.js
文件,然后加上以下内容:(如果已经有此文件就直接修改)
module.exports = {
publicPath: , // 相对于 HTML 页面(目录相同)
}
相同点: assets
和 static
两个都是存放静态资源文件。项目中所需要的资源文件图片,字体图标,样式文件等都可以放在这两个文件下,这是相同点
不相同点:assets
中存放的静态资源文件在项目打包时,也就是运行 npm run build
时会将 assets
中放置的静态资源文件进行打包上传,所谓打包简单点可以理解为压缩体积,代码格式化。而压缩后的静态资源文件最终也都会放置在 static
文件中跟着 index.html
一同上传至服务器。static
中放置的静态资源文件就不会要走打包压缩格式化等流程,而是直接进入打包好的目录,直接上传至服务器。因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是 static
中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于 assets
中打包后的文件提交较大点。在服务器中就会占据更大的空间。
建议: 将项目中 template
需要的样式文件 js
文件等都可以放置在 assets
中,走打包这一流程。减少体积。而项目中引入的第三方的资源文件如iconfoont.css
等文件可以放置在 static
中,因为这些引入的第三方文件已经经过处理,我们不再需要处理,直接上传。
Controller
;Model
改变状态;View
,用户得到反馈。View
的变动,自动反映在 ViewModel
,反之亦然。MVVM
是 Model-View-ViewModel
的缩写。Model
代表数据模型,也可以在 Model
中定义数据修改和操作的业务逻辑。view
代表 UI 组件,它负责将数据模型转化成 UI 展现出来。ViewModel
监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View
和 Model
的对象,连接 Model
和 View
。MVVM
架构下,View
和 Model
之间并没有直接的联系,而是通过ViewModel
进行交互,Model
和 ViewModel
之间的交互是双向的, 因此 View
数据的变化会同步到 Model
中,而 Model
数据的变化也会立即反应到 View
上。ViewModel
通过双向数据绑定把 View
层和 Model
层连接了起来,而 View
和 Model
之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM
, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM
来统一管理。数据劫持结合“发布者 - 订阅者”模式的方式
,通过 Object.defineProperty()
来劫持各个属性的 setter
、 getter
,在数据变动时发布消息给订阅者,触发相应监听回调。action
来维护对应的 state
。vue双向数据绑定
是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;核心:关于 vue双向数据绑定
,其核心是 Object.defineProperty()
方法。
Vue
路由在Android机上有问题,babel
问题,安装babel
polypill
插件解决。
方法一: 只用a标签,不适用button标签;
方法二: 使用button标签和Router.navigate方法
使用 @click.native
。原因:router-link
会阻止click
事件,.native
指直接监听一个原生事件。
vue-router
模块的 router-link
组件。children
数组来定义子路由
使用 location.href= /url
来跳转,简单方便,但是刷新了页面;使用 history.pushState( /url )
,无刷新页面,静态跳转;引进 router
,然后使用 router.push( /url )
来跳转,使用了 diff
算法,实现了按需加载,减少了 dom
的消耗。其实使用 router
跳转和使用 history.pushState()
没什么差别的,因为 vue-router
就是用了 history.pushState()
,尤其是在 history
模式下。
vue-router
默认使用 hash
模式,所以在路由加载的时候,项目中的 URL 会自带 “#”。如果不想使用 “#”, 可以使用 vue-router
的另一种模式 history:new Router ({ mode : 'history', routes: [ ]})
。"#"
, #
以及 #
后面的字符称之为 hash
,用 window.location.hash
读取;
hash
虽然在 URL
中,但不被包括在 HTTP
请求中;用来指导浏览器动作,对服务器安全无用, hash
不会重加载页面。hash
模式下,仅hash
符号之间的内容会被包含在请求中,如 http://www.xxx.com
,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。history
采用 HTML5
的新特性 History Interface
中新增的 pushState()
, replaceState()
可以对浏览器历史记录栈进行修改,以及 popState
事件的监听到状态变更。history
模式下,前端的 URL
必须和实际向后端发起请求的 URL
一致,如 http://www.xxx.com/items/id
。后端如果缺少对 /items/id
的路由处理,将返回 404
错误。Vue-Router
官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL
匹配不到任何静态资源,则应该返回同一个 index.html
页面,这个页面就是你 app
依赖的页面。”$router
为VueRouter
实例,想要导航到不同URL
,则使用$router.push
方法。返回上一个历史history
用 $router.to(-1)
$route
为当前router
跳转对象里面可以获取 name
、 path
、query
、 params
等。第一种: 是全局导航钩子:router.beforeEach(to,from,next)
,作用:跳转前进行判断拦截。
+ beforeEach 主要有3个参数 to ,from ,next
。 作用:跳转前进行判断拦截
+ to:route
即将进入的目标路由对象,
+ from:route
当前导航正要离开的路由
+ next:function
一定要调用该方法 resolve
这个钩子。执行效果依赖 next
方法的调用参数。可以控制网页的跳转。
第二种: 组件内的钩子
第三种: 单独路由独享组件
第一种: vue
异步组件技术 ==== 异步加载,vue-router
配置路由 , 使用vue
的异步组件技术 , 可以实现按需加载 .但是,这种情况下一个组件生成一个js
文件。
第二种: 路由懒加载(使用import
)。
第三种: webpack
提供的require.ensure()
,vue-router
配置路由,使用webpack
的require.ensure
技术,也可以实现按需加载。这种情况下,多个路由指定相同的chunkName
,会合并打包成一个js
文件。
用法: query
要用path
来引入,params
要用name
来引入,接收参数都是类似的,分别是 this.$route.query.name
和 this.$route.params.name
。url
地址显示:query
更加类似于我们ajax
中get
传参,params
则类似于post
,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
注意点: query
刷新不会丢失query
里面的数据 params
刷新会丢失 params
里面的数据。
引用地址: https://segmentfault.com/a/1190000012996217
slot
,是组件的一块HTML
模板,这块模板显示不显示、以及怎样显示由父组件来决定。 实际上,一个slot
最核心的两个问题这里就点出来了,是显示不显示和怎样显示。html
模板,指的是div、span、ul、table
这些,非插槽模板的显示与隐藏以及怎样显示由插件自身控制;插槽模板是slot
,它是一个空壳子,因为它显示与隐藏以及最后用什么样的html
模板显示由父组件控制。但是插槽显示的位置确由子组件自身决定,slot
写在组件template
的哪块,父组件传过来的模板将来就显示在哪块。SEO
难度较高(建议通过服务端来进行渲染组件)DOM
渲染之前执行。
DOM
已创建完成,在数据渲染前最后一次更改数据。
DOM
挂载完成。
DOM
也重新 render
完成,更改数据会陷入死循环。
Vue
实例从运行阶段进入到了销毁阶段,这个时候上所有的 data
和 methods
, 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁data
和 methods
, 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。beforeCreate, created, beforeMount, mounted
props
属性,绑定父组件数据,实现双方通信。$emit
触发。let event = new Vue ()
;/* 监听事件 */
event.$on( 'eventName' , (val) => { //......do something });
/* 触发事件 */
event.$emit( 'eventName' , 'this is amessage' );
store
中; 改变状态的方式是提交 mutations
,这是个同步的事物; 异步逻辑应该封装在action
中。main.js
引入store
,注入。新建了一个目录store
,….. export
。state => 基本数据(数据源存放地)
Vuex
使用单一状态树,即每个应用将仅仅包含一个store
实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations => 提交更改数据的方法,同步!
mutations
定义的方法动态修改Vuex
的 store
中的状态或数据。
getters => 从基本数据派生出来的数据
类似vue
的计算属性,主要用来过滤一些数据。
action => 像一个装饰器,包裹mutations
,使之可以异步。
actions
可以理解为通过将mutations
里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath
来分发 action
。
modules => 模块化Vuex
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,
使得结构非常清晰,方便管理。
vuex
的 state
里。action
里,方便复用。$nextTick
是在下次DOM
更新循环结束之后执行延迟回调,在修改数据之后使用$nextTcik
,则可以在回调中获取更新后的DOM
。data
的值然后马上获取这个 dom
元素的值,是不能获取到更新后的值, 你需要使用 $nextTick
这个回调,让修改后的 data
值渲染更新到 dom
元素之后在获取,才能成功。v-show = false
隐藏起来的输入框,并获取焦点。showsou(){
this.showit = true //修改 v-show
document.getElementById("keywords").focus()
//在第一个 tick 里,获取不到输入框,自然也获取不到焦点
}
修改为:
showsou(){
this.showit = true
this.$nextTick(function () {
// DOM 更新了
document.getElementById("keywords").focus()
})
}
(https://cn.vuejs.org/v2/guide/reactivity.html)
。JS
本身的特性带来的,如果 data
是一个对象,那么由于对象本身属于引用类型,当我们修改其中的一个属性时,会影响到所有 Vue
实例的数据,如果将 data
作为一个函数返回一个对象,那么每一个实例的 data
属性都是独立的,不会互相影响了。method
或者一个computed
,对于最终结果,两种方式是相同的。method
调用总会执行该函数(场景:方法的调用)。Vue
实例上的数据变动(场景:监听路由地址,搜索数据)jQuery
专注视图层,通过操作 DOM
去实现页面的一些逻辑渲染;Vue
专注于数据层,通过数据的双向绑定,最终表现在 DOM
层面,减少了 DOM
操作。Vue
将数据和View完全分离,使用了组件化思想,使得项目子集职责清晰,提高了开发效率,方便重复利用,便于协同开发。
Vue.filter()
注册一个自定义过滤器,它接收两个参数:过滤器 ID 和过滤器函数。过滤器函数以值为参数,返回转换后的值。
{{通过管道传递的数据 | 对应过滤器的方法名('参数1',‘参数2’)}}
{{price | myCurrency('¥',‘$’)}}