上家公司的專案主要是使用jQuery和Angular1,然後自己學了React,沒想到來到這家公司突然開始做vue,不過vue還是挺容易上手的。以下是vue技術棧的一些總結,都是來自官網,主要是自己對vue技術棧知識點的一些整理,因此此文很水,建議閱讀我的上一篇文章Vuejs技術棧從CLI到打包上線實戰全解析
有兩種建置方式,獨立建置和運行建置。它們的區別在於前者包含模板編譯器而後者不包含。
模板編譯器的職責是將模板字串編譯為純JavaScript的渲染函數。如果你想要在元件中使用template選項,你就需要編譯器。
具體查看官網的流程圖,要注意的是created和mounted區別,created是vm實例已建立但未掛載,因此一些DOM操作應該放在mounted中。非同步請求放在created或mounted暫時沒發現什麼區別,如您知道有什麼區別,請評論指出。
模板內的表達式不應該包含太多的邏輯,對於任何複雜邏輯,都應使用計算屬性
computed屬性和methods不同的是計算屬性是基於它們的依賴進行快取的。
computed屬性和computed屬性,通常更好的想法是使用computed屬性而不是命令式的watch回呼。雖然計算屬性在大多數情況下更合適,但有時也需要一個自訂的watcher。當你想要在資料變化回應時,執行非同步操作或開銷較大的操作,這是很有用的。
陣列的變異方法(mutation method,會改變被這些方法呼叫的原始陣列)會觸發視圖更新,有以下七個:
push() pop() shift() unshift() splice() sort() reverse()
當使用非變異方法時,可以用新數組取代舊數組,或使用Vue.set方法。
可以用新物件取代舊對象,或是使用Vue.set方法
Vue.set(vm.someObject, 'foo', 'bar') this.someObject = Object.assign({}, this.someObject, { a:1, b:2 })
Vue.js為v- on提供了事件修飾符和按鍵修飾符
可以用v-model指令在表單控制項元素上建立雙向資料綁定。常見修飾符有.lazy、.number、.trim。
也可以使用自訂事件的表單輸入元件。
Vue元件的API來自三個部分:props,events和slots:
Props允許外部環境傳遞資料給元件
Events允許元件觸發外部環境的副作用
Slots允許外部環境將額外的內容組合在元件中。
1)元件的data屬性必須是函數
2)父子元件
在Vue.js中,父子元件的關係可以總結為props down, events up 。父元件透過props向下傳遞資料給子元件,子元件透過events給父元件發送訊息。
prop是單向綁定的:當父元件的屬性變化時,將傳導給子元件,但不會反過來。這是為了防止子元件無意修改了父元件的狀態——這會讓應用程式的資料流難以理解。
另外,每次父元件更新時,子元件的所有prop都會更新為最新值。這意味著你不應該在子元件內部改變prop。如果你這麼做了,Vue會在控制台給予警告。
為什麼我們會有修改prop中資料的衝動呢?通常是這兩種原因:
1.prop作為初始值傳入後,子元件想把它當作局部資料來用;
2.prop作為初始值傳入,由子組件處理成其它資料輸出。
對這兩個原因,正確的因應方式是:
1.定義一個局部變量,並用prop的值初始化它:
props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }
2.定義一個計算屬性,處理prop的值並傳回。
props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }
注意在JavaScript中物件和陣列是參考類型,指向同一個記憶體空間,如果prop是物件或陣列,在子元件內部改變它會影響父元件的狀態。
3)非父子元件
有時候兩個元件也需要通訊(非父子關係)。在簡單的場景下,可以使用空的Vue實例作為中央事件匯流排。在複雜的情況下,我們應該考慮使用專門的狀態管理模式。
4).sync修飾符
在某些情況下,我們可能會需要對一個prop進行『雙向綁定』。
2.0中移除了.sync,Vue2.3.0+又將其加回來了,但是這次它只是作為一個編譯時的語法糖存在,它會被擴展為一個自動更新父組件屬性的v-on偵聽器。如下程式碼
<comp :foo.sync="bar"></comp>
會被擴展為:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
當子元件需要更新foo的值時,它需要明確地觸發一個更新事件:
this.$emit('update:foo', newValue)
5)使用slot進行內容分發
作用域插槽:接收從子元件傳遞的prop物件。作用域插槽更具代表性的用例是列表元件,允許元件自訂應該如何渲染列表每一項
6)動態元件、is特性和keep-alive指令
7 )子元件索引
尽管有props和events,但是有时仍然需要JavaScript中直接访问子组件。为此可以使用ref为子组件指定一个索引ID。
虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
Vue在插入、更新或者移除DOM时,提供多种不同方式的应用过渡效果。包括以下工具:
在CSS过渡和动画中自动应用class
可以配合使用第三方CSS动画库,如Animate.css
在过渡钩子函数中使用JavaScript直接操作DOM
可以配合使用第三方JavaScript动画库,如Velocity.js
1)单元素/组件的过渡
Vue提供了transition的封装组件,在下列情形中,可以给任何元素和组件添加过渡
条件渲染(使用v-if)
条件展示(使用v-show)
动态组件
组件根节点
2)多个元素的过渡
对于原生标签可以使用 v-if/v-else
3)多个组件的过渡
多个组件的过渡我们可以使用动态组件。
4)列表过渡
和Angular的指令类似,主要操作DOM,下面是一个滚动加载的指令,holder暂时没想到什么更好的处理方法:
let scrollCallback = function (callback) { let windowH = window.innerHeight let getDocumentHeight = function () { var body = document.body var html = document.documentElement return Math.max( body.offsetHeight, body.scrollHeight, html.clientHeight, html.offsetHeight, html.scrollHeight ) } let scrollH = document.documentElement.scrollTop || document.body.scrollTop if (windowH + scrollH >= getDocumentHeight() - (this.holder || 20)) { callback() } } let callBackWarpped export default { bind (el, binding, vnode) { let holder if (vnode.data && vnode.data.attrs && vnode.data.attrs['scroll-placeholder']) { holder = parseInt(vnode.data.attrs['scroll-placeholder']) } else { holder = 20 } callBackWarpped = scrollCallback.bind({el, holder}, binding.value) window.addEventListener('scroll', callBackWarpped, false) }, unbind: function () { window.removeEventListener('scroll', callBackWarpped, false) } }
混合是一种灵活的分布式复用Vue组件的方式。混合对象可以包含任意组件选项。以组件使用混合对象时,所有混合对象的选项将被混入该组件本身的选项。
1)创建插件
Vue.js的插件应当有一个公开方法install。这个方法的第一个参数是Vue构造器 , 第二个参数是一个可选的选项对象。
2)使用插件
通过全局方法Vue.use()使用插件:
// 调用 `MyPlugin.install(Vue)` Vue.use(MyPlugin)
也可以传入一个选项对象:
Vue.use(MyPlugin, { someOption: true })
1)router-link声明式导航
<router-link to="/foo">Go to Foo</router-link>
router-link对应的路由匹配成功,将自动设置class属性值.router-link-active。
2)编程式导航
// 字符串 router.push('home') // 对象 router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: 123 }}) // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }})
vue-router默认hash模式,也可以设置为路由的history模式。
vue-router提供的导航钩子主要用来拦截导航,让它完成跳转或取消。有多种方式可以在路由导航发生时执行钩子:全局的, 单个路由独享的, 或者组件级的。
一个路由匹配到的所有路由记录会暴露为$route对象(还有在导航钩子中的route对象)的$route.matched数组。因此,我们需要遍历$route.matched来检查路由记录中的meta字段。
router-view是基本的动态组件,所以我们可以用transition组件给它添加一些过渡效果。
有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。
导航完成之前获取:导航完成前,在路由的enter钩子中获取数据,在数据获取成功后执行导航。
注意: 这个功能只在HTML5 history模式下可用。
当打包构建应用时,Javascript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
结合Vue的异步组件和Webpack的code splitting功能,轻松实现路由组件的懒加载。
const Foo = resolve => require(['./Foo.vue'], resolve) const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ] })
可以设置tag、append、active-class、exact等属性
有时候我们要让 "激活时的CSS类名" 应用在外层元素,而不是a标签本身,那么可以用router-link渲染外层元素,包裹着内层的原生a标签:
<router-link tag="li" to="/foo"> <a>/foo</a> </router-link>
在这种情况下,a将作为真实的链接(它会获得正确的 href 的),而"激活时的CSS类名"则设置到外层的li。
routes、mode、base、linkActiveClass、scrollBehavior
1)注入的属性
通过在Vue根实例的router配置传入router实例,下面两个属性成员会被注入到每个子组件。
$router:router实例
$route:当前激活的路由信息对象。这个属性是只读的,里面的属性是immutable(不可变)的,不过你可以watch(监测变化)它。
2)允许的额外配置:beforeRouteEnter、beforeRouteLeave
1)单一状态树
Vuex使用单一状态树--是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个『唯一数据源(SSOT)』而存在。这也意味着,每个应用将仅仅包含一个store实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
2)在Vue组件中获得Vuex状态
最好在根实例中注册store选项,该store实例会注入到根组件下的所有子组件中,且子组件能通过this.$store访问到。而不是在每个需要使用state的组件中需要频繁地导入。
3)mapState辅助函数
当一个组件需要获取多个状态时候,可以使用mapState辅助函数帮助我们生成计算属性,这样可以简化代码书写。mapState函数会返回一个对象,然后可以使用对象展开运算符将它与局部计算属性混合使用。
4)不要滥用vuex
使用Vuex并不意味着你需要将所有的状态放入Vuex。虽然将所有的状态放到Vuex会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
getters用来从store中的state中派生出一些状态,例如对列表进行过滤并计数:
computed: { doneTodosCount () { return this.$store.state.todos.filter(todo => todo.done).length } }
getters可以认为是store的计算属性。和state类似,有mapGetters辅助函数。
更改Vuex的store中的状态的唯一方法是提交mutation。Vuex中的mutations非常类似于事件:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受state作为第一个参数
1)提交载荷(Payload)
你可以向store.commit传入额外的参数,即mutation的载荷(payload):
// ... mutations: { increment (state, n) { state.count += n } } store.commit('increment', 10)
2)Mutations需遵守Vue的响应规则
3)使用常量替代Mutation事件类型
4)mutation必须是同步函数
5)在组件中提交Mutations
你可以在组件中使用this.$store.commit('xxx')提交mutation,或者使用mapMutations辅助函数将组件中的methods映射为store.commit调用(需要在根节点注入store)。
actions类似于mutation,不同在于:
actions提交的是mutation,而不是直接变更状态。
actions可以包含任意异步操作。
1)在组件中分发Action
你在组件中使用this.$store.dispatch('xxx')分发action,或者使用mapActions辅助函数将组件的methods映射为store.dispatch调用(需要先在根节点注入store)
2)组合Actions
Action通常是异步的,那么如何知道action什么时候结束呢?更重要的是,我们如何才能组合多个action,以处理更加复杂的异步流程?
首先,你需要明白store.dispatch可以处理被触发的action的回调函数返回的Promise,并且store.dispatch仍旧返回Promise。
使用async/await会更加简单:
// 假设 getData() 和 getOtherData() 返回的是 Promise actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。
为了解决以上问题,Vuex允许我们将store分割成模块(module)。每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
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 的状态
Vuex的store接受plugins选项,这个选项暴露出每次mutation的钩子。Vuex插件就是一个函数,它接收store作为唯一参数。
开启严格模式,仅需在创建store的时候传入strict:true
在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更--请确保在发布环境下关闭严格模式,以避免性能损失。
以上是關於Vuejs技術堆疊的知識匯總的詳細內容。更多資訊請關注PHP中文網其他相關文章!