赞
踩
- const timer = setInterval(() =>{
- // 某些定时器操作
- }, 500);
- // 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
- this.$once('hook:beforeDestroy', () => {
- clearInterval(timer);
- })
<template></template>
有什么用?- 包裹嵌套其它元素,使元素具有区域性,自身具有三个特点:
- *隐藏性:不会显示在页面中
- *任意性:可以写在页面的任意地方
- *无效性: 没有一个根元素包裹,任何HTML内容都是无效的
- jsx不是一门新的语言,是一种新的语法糖。让我们在js中可以编写像html一样的代码。
- 允许XML语法直接加入到JavaScript代码中,让你能够高效的通过代码而不是模板来定义界面
- 1.如果需要响应式,考虑使用虚表(只渲染要显示的数据);
- 2.如果不考虑响应式,变量在beforeCreated或created中声明(Object.freeze会导致列表无法增加数据)
页面关闭、路由跳转、v-if和改变key值
需要,原生DOM事件必须要手动销毁,否则会造成内存泄漏
- Object.defineProperty定义新属性或修改原有的属性;
- vue的数据双向绑定的原理就是用的Object.defineProperty这个方法,里面定义了setter和getter方法,通过观察者模式(发布订阅模式)来监听数据的变化,从而做相应的逻辑处理。
- 通过
-
- this.$parent
- 通过
-
- this.$root
this.$refs
组件自己调用自己,场景有用于生成树形结构菜单
- v-for循环,利用下标和v-show显示
-
- `<div id="app">
- <ul class="tabs">
- <li class="li-tab" v-for="(item,index) in tabsParam"
- @click="toggleTabs(index)"
- :class="index===nowIndex?'active':''">{{item}}</li>
- </ul>
- <div class="divTab" v-show="nowIndex===0">我是tab1</div>
- <div class="divTab" v-show="nowIndex===1">我是tab2</div>
- <div class="divTab" v-show="nowIndex===2">我是tab3</div>
- <div class="divTab" v-show="nowIndex===3">我是tab4</div>
- </div>`
我的做法就是让app在webview把app的方法暴露在window上让前端调用、反之app调用前端的方法也需要前端把方法暴露在window上(window.xxx = 方法或值 ),页面销毁的时候移除方法(delete window.xxx)
- vue中使用匿名函数,会出现this指针改变。
- 解决方法
- 1.使用箭头函数
- 2.定义变量绑定this至vue对象
- 定义组件名有两种方式:
- 1.kebab-case(短横线分隔命名),引用时必须也采用kebab-case;
- 2.PascalCase(首字母大写命名),引用时既可以采用PascalCase也可以使用kebab-case;
- 但在DOM中使用只有kebab-case是有效的
- 配置ts-loader,tsconfig
- 增加类型扩展,让ts识别vue文件
- vue文件中script里面换成ts写法, 需要增加几个ts扩展的package, 比如vue-property-decorator
-
-
- vue中is的属性引入是为了解决dom结构中对放入html的元素有限制的问题
-
- <ul>
- <li is='my-component'></li>
- </ul>
- :class 绑定变量 绑定对象 绑定一个数组 绑定三元表达式
- :style 绑定变量 绑定对象 绑定函数返回值 绑定三元表达式
- 函数式组件:
-
- 需要提供一个render方法, 接受一个参数(createElement函数), 方法内根据业务逻辑,通过createElement创建vnodes,最后return vnodes
-
- createElement函数, 三个参数, 第一个参数是html标签或自定义组件,第二个参数一个obj(包含props, on...等等), 第三个参数children(通过createElement构建, 或者字符串)
- optionMergeStrategies
-
- 类型:{ [key: string]: Function }
-
- 默认值:{}
-
- 用法:
-
- Vue.config.optionMergeStrategies._my_option = function (parent, child, vm) {
- return child + 1
- }
-
- const Profile = Vue.extend({
- _my_option: 1
- })
-
- // Profile.options._my_option = 2
-
- 自定义合并策略的选项。
-
- 合并策略选项分别接收在父实例和子实例上定义的该选项的值作为第一个和第二个参数,Vue 实例上下文被作为第三个参数传入。
- 项目使用keep-alive时,可搭配组件name进行缓存过滤
- DOM做递归组件时需要调用自身name
- vue-devtools调试工具里显示的组见名称是由vue中组件name决定的
- 通过在父组件中inject一些数据然后再所有子组件中都可以通过provide获取使用该参数,
-
- 主要是为了解决一些循环组件比如tree, menu, list等, 传参困难, 并且难以管理的问题, 主要用于组件封装, 常见于一些ui组件库
- 通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理
-
- 如果父组件在使用到一个复用组件的时候,获取这个组件在不同地方有少量的更改,如果去重写组件是一件不明智的事情
-
- 通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用
-
- 比如布局组件、表格列、下拉选、弹框显示内容等
通过 Vue 的 元素加一个特殊的 is 特性来实现
有,devtools确实是个好东西,大力协助vue项目开发,传参,数据展示,用于调试vue应用,这可以极大地提高我们的调试效率
- props:{
- title:String,
- likes: Number,
- isPublished: Boolean,
- commentIds: Array,
- author: Object,
- callback: Function,
- contactsPromise: Promise
- }
单个类型就用Number等基础类型,多个类型用数组,必填的话设置require为true,默认值的话设置default,对象和数组设置默认用工厂函数,自定义验证函数validator。
可以在路由meta中加入参数, 对打开的路由进行keep-alive的判断, 通过钩子active等
- 第一: 容错处理, 这个要做好, 极端场景要考虑到, 不能我传错了一个参数你就原地爆炸
- 第二: 缺省值(默认值)要有, 一般把应用较多的设为缺省值
- 第三: 颗粒化, 把组件拆分出来.
- 第四: 一切皆可配置, 如有必要, 组件里面使用中文标点符号, 还是英文的标点符号, 都要考虑到
- 第五: 场景化, 如一个dialog弹出, 还需要根据不同的状态封装成success, waring, 等
- 第六: 有详细的文档/注释和变更历史, 能查到来龙去脉, 新版本加了什么功能是因为什么
- 第七: 组件名称, 参数prop, emit, 名称设计要通俗易懂, 最好能做到代码即注释这种程度
- 第八: 可拓展性, 前期可能不需要这个功能, 但是后期可能会用上, 要预留什么, 要注意什么, 心里要有逼数
- 第九: 规范化,我这个input组件, 叫on-change, 我另外一个select组件叫change, 信不信老子捶死你
- 第十: 分阶段: 不是什么都要一期开发完成看具体业务, 如果一个select, 我只是个简单的select功能, 什么multi老子这个版本压根不需要, 别TM瞎折腾! 给自己加戏
- 补充下2楼:
- ssr直出,
- webpack压缩HTML/CSS/JS,
- 首屏css单独提取内联,
- 关键资源Proload,
- 图片:不缩放,使用webp、小图片base64,iconfont,
- gzip,
- dns-prefetch,
- 静态资源单独域名,去掉cookie
- vendor.js, app.js, app.css,
- 1.xxx.js
- 2.xxx.js
-
- 如果有设置到单独提取css的话
- 还有
- 1.xxx.css
- ......
- 你说的是vue内部的源码对Array数据的中转代理嘛
- 好像对push, shift等通用方法都做了代理吧!
- 因为它对中转的数据都做了监听
- 1.打包后文件引用路径不对,导致找不到文件报错白屏
- 2.路由模式mode设置影响
我的理解:计算出虚拟 DOM 中真正变化的部分,并且只针对该部分进行 DOM 更新,而非重新渲染整个页面
- ajax, 实际上就是xmlHttpRequest, 旧瓶装新酒的一种新应用的称呼
- fetch是新出的规范, 具体实现原理不太清楚, 但是内部返回的是一个Promise
- axios是基于ajax的再次封装返回的也是Promise
- @click=“func” 默认第一个参数传入event对象
- @click="func(0, $event)" 如果自己需要传入参数和event对象,则需要使用$event来获取event对象并传入func
vant,mint等等吧,各有各的坑,不过大部分都是可以查到解决方案的
- 提到DOM的更新是异步执行的,只要数据发生变化,将会开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
-
- 简单来说,就是当数据发生变化时,视图不会立即更新,而是等到同一事件循环中所有数据变化完成之后,再统一更新视图。
-
- 关于异步的解析,可以查看阮一峰老师的这篇文章,具体来说,异步执行的运行机制如下。
-
- (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
-
- (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
-
- (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
-
- (4)主线程不断重复上面的第三步。
-
- 例子:
-
- //改变数据
- vm.message = 'changed'
-
- //想要立即使用更新后的DOM。这样不行,因为设置message后DOM还没有更新
- console.log(vm.$el.textContent) // 并不会得到'changed'
-
- //这样可以,nextTick里面的代码会在DOM更新后执行
- Vue.nextTick(function(){
- console.log(vm.$el.textContent) //可以得到'changed'
- })
- v-cloak指令只是在标签中加入一个v-cloak自定义属性,在HTML还编译完成之后该属性会被删除。
- v-pre可以用来阻止预编译,有v-pre指令的标签内部的内容不会被编译,会原样输出。
- {{item.title}}
-
- <dl v-if="item.list.length > 0">
- <dd v-for="(item2,index2) in item.list" :index="index2" :key="item2.title">
- <router-link :to="item2.route" exact>{{item2.title}}</router-link>
- </dd>
- </dl>
-
- </li>
- </ul>
SSR server side render服务端渲染,解决spa应用缺点的首屏加载速度慢、不利于SEO问题
- 单向数据流:所有状态的改变可记录、可跟踪,源头易追溯;所有数据只有一份,组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性;一旦数据变化,就去更新页面(data-页面),但是没有(页面-data);如果用户在页面上做了变动,那么就手动收集起来(双向是自动),合并到原有的数据中。
- 双向数据流:无论数据改变,或是用户操作,都能带来互相的变动,自动更新。
vue 没用过echarts react到是用过 不过我想应该差不多 ,多注意dom的渲染时机 和chart的实例化时机 在相应的生命周期方法中做操作。结合强制刷新 应该就能解决大部分问题
start
Vue 项目性能优化 — 实践指南(网上最全 / 详细)
前言Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题,所以我们仍然需要去关注 Vue 项目性能方面的优化,使项目具有更高效的性能、更好的用户体验。本文是作者通过实际项目的优化实践进行总结而来,希望读者读完本文,有一定的启发思考,从而对自己的项目进行优化起到帮助。本文内容分为以下三部分组成:
- Vue 代码层面的优化;
- webpack 配置层面的优化;
- 基础的 Web 技术层面的优化。
辛苦整理良久,还望手动点赞鼓励~
github地址为:github.com/fengshi123/…,汇总了作者的所有博客,也欢迎关注及 star ~
一、代码层面的优化
1.1、v-if 和 v-show 区分使用场景v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。
1.2、computed 和 watch 区分使用场景computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
-
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
1.3、v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
(1)v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff 。
(2)v-for 遍历避免同时使用 v-if
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
推荐:
- {{ user.name }}
computed: { activeUsers: function () { return this.users.filter(function (user) { return user.isActive }) } }
不推荐:
- {{ user.name }}
1.4、长列表性能优化
Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
- export default {
- data: () => ({
- users: {}
- }),
- async created() {
- const users = await axios.get("/api/users");
- this.users = Object.freeze(users);
- }
- };
1.5、事件的销毁
Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。 如果在 js 内使用 addEventListene 等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露,如:
- created() {
- addEventListener(‘click’, this.click, false)
- },
- beforeDestroy() {
- removeEventListener(‘click’, this.click, false)
- }
1.6、图片资源懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件:
(1)安装插件
npm install vue-lazyload --save-dev
(2)在入口文件 man.js 中引入并使用
import VueLazyload from ‘vue-lazyload’
然后再 vue 中直接使用
Vue.use(VueLazyload)
或者添加自定义选项
- Vue.use(VueLazyload, {
- preLoad: 1.3,
- error: ‘dist/error.png’,
- loading: ‘dist/loading.gif’,
- attempt: 1
- })
(3)在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示:
以上为 vue-lazyload 插件的简单使用,如果要看插件的更多参数选项,可以查看 vue-lazyload 的 github 地址。
1.7、路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
路由懒加载:
- const Foo = () => import(’./Foo.vue’)
- const router = new VueRouter({
- routes: [
- { path: ‘/foo’, component: Foo }
- ]
- })
1.8、第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 element-ui 组件库为例:
(1)首先,安装 babel-plugin-component :
npm install babel-plugin-component -D
(2)然后,将 .babelrc 修改为:
- {
- “presets”: [[“es2015”, { “modules”: false }]],
- “plugins”: [
- [
- “component”,
- {
- “libraryName”: “element-ui”,
- “styleLibraryName”: “theme-chalk”
- }
- ]
- ]
- }
(3)在 main.js 中引入部分组件:
- import Vue from ‘vue’;
- import { Button, Select } from ‘element-ui’;
-
- Vue.use(Button)
- Vue.use(Select)
1.9、优化无限列表性能
如果你的应用存在非常长或者无限滚动的列表,那么需要采用 窗口化 的技术来优化性能,只需要渲染少部分区域的内容,减少重新渲染组件和创建 dom 节点的时间。 你可以参考以下开源项目 vue-virtual-scroll-list 和 vue-virtual-scroller 来优化这种无限列表的场景的。
1.10、服务端渲染 SSR or 预渲染
服务端渲染是指 Vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的 html 片段直接返回给客户端这个过程就叫做服务端渲染。
(1)服务端渲染的优点:
- 更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
-
- 更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
(2)服务端渲染的缺点:
- 更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
-
- 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源,因此如果你预料在高流量环境下使用,请准备相应的服务器负载,并明智地采用缓存策略。
如果你的项目的 SEO 和 首屏渲染是评价项目的关键指标,那么你的项目就需要服务端渲染来帮助你实现最佳的初始加载性能和 SEO,具体的 Vue SSR 如何实现,可以参考作者的另一篇文章《Vue SSR 踩坑之旅》。如果你的 Vue 项目只需改善少数营销页面(例如 /, /about, /contact 等)的 SEO,那么你可能需要预渲染,在构建时 (build time) 简单地生成针对特定路由的静态 HTML 文件。优点是设置预渲染更简单,并可以将你的前端作为一个完全静态的站点,具体你可以使用 prerender-spa-plugin 就可以轻松地添加预渲染 。
二、Webpack 层面的优化
2.1、Webpack 对图片进行压缩
在 vue 项目中除了可以在 webpack.base.conf.js 中 url-loader 中设置 limit 大小来对图片处理,对小于 limit 的图片转化为 base64 格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader来压缩图片:
(1)首先,安装 image-webpack-loader :
npm install image-webpack-loader --save-dev
(2)然后,在 webpack.base.conf.js 中进行配置:
- {
- test: /.(png|jpe?g|gif|svg)(?.*)?$/,
- use:[
- {
- loader: ‘url-loader’,
- options: {
- limit: 10000,
- name: utils.assetsPath(‘img/[name].[hash:7].[ext]’)
- }
- },
- {
- loader: ‘image-webpack-loader’,
- options: {
- bypassOnDebug: true,
- }
- }
- ]
- }
2.2、减少 ES6 转为 ES5 的冗余代码
Babel 插件会在将 ES6 代码转换成 ES5 代码时会注入一些辅助函数,例如下面的 ES6 代码:
class HelloWebpack extends Component{…}
这段代码再被转换成能正常运行的 ES5 代码时需要以下两个辅助函数:
- babel-runtime/helpers/createClass // 用于实现 class 语法
- babel-runtime/helpers/inherits // 用于实现 extends 语法
在默认情况下, Babel 会在每个输出文件中内嵌这些依赖的辅助函数代码,如果多个源代码文件都依赖这些辅助函数,那么这些辅助函数的代码将会出现很多次,造成代码冗余。为了不让这些辅助函数的代码重复出现,可以在依赖它们时通过 require(‘babel-runtime/helpers/createClass’) 的方式导入,这样就能做到只让它们出现一次。babel-plugin-transform-runtime 插件就是用来实现这个作用的,将相关辅助函数进行替换成导入语句,从而减小 babel 编译出来的代码的文件大小。
(1)首先,安装 babel-plugin-transform-runtime :
npm install babel-plugin-transform-runtime --save-dev
(2)然后,修改 .babelrc 配置文件为:
- “plugins”: [
- “transform-runtime”
- ]
如果要看插件的更多详细内容,可以查看babel-plugin-transform-runtime 的 详细介绍。
2.3、提取公共代码
如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:
- 相同的资源被重复加载,浪费用户的流量和服务器的成本。
- 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。Webpack 内置了专门用于提取多个Chunk 中的公共部分的插件 CommonsChunkPlugin,我们在项目中 CommonsChunkPlugin 的配置如下:
// 所有在 package.json 里面依赖的包,都会被打包进 vendor.js 这个文件中。
- new webpack.optimize.CommonsChunkPlugin({
- name: ‘vendor’,
- minChunks: function(module, count) {
- return (
- module.resource &&
- /.js$/.test(module.resource) &&
- module.resource.indexOf(
- path.join(__dirname, ‘…/node_modules’)
- ) === 0
- );
- }
- }),
- // 抽取出代码模块的映射关系
- new webpack.optimize.CommonsChunkPlugin({
- name: ‘manifest’,
- chunks: [‘vendor’]
- })
如果要看插件的更多详细内容,可以查看 CommonsChunkPlugin 的 详细介绍。
2.4、模板预编译
当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种用法。
预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。
如果你使用 webpack,并且喜欢分离 JavaScript 和模板文件,你可以使用 vue-template-loader,它也可以在构建过程中把模板文件转换成为 JavaScript 渲染函数。
2.5、提取组件的 CSS
当使用单文件组件时,组件内的 CSS 会以 style 标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,如果你使用服务端渲染,这会导致一段 “无样式内容闪烁 (fouc) ” 。将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存。
查阅这个构建工具各自的文档来了解更多:
- webpack + vue-loader ( vue-cli 的 webpack 模板已经预先配置好)
- Browserify + vueify
- Rollup + rollup-plugin-vue
2.6、优化 SourceMap
我们在项目进行打包后,会将开发中的多个文件代码打包到一个文件中,并且经过压缩、去掉多余的空格、babel编译化后,最终将编译得到的代码会用于线上环境,那么这样处理后的代码和源代码会有很大的差别,当有 bug的时候,我们只能定位到压缩处理后的代码位置,无法定位到开发环境中的代码,对于开发来说不好调式定位问题,因此 sourceMap 出现了,它就是为了解决不好调式代码问题的。
SourceMap 的可选值如下(+ 号越多,代表速度越快,- 号越多,代表速度越慢, o 代表中等速度 )
开发环境推荐: cheap-module-eval-source-map
生产环境推荐: cheap-module-source-map
原因如下:
- cheap: 源代码中的列信息是没有任何作用,因此我们打包后的文件不希望包含列相关信息,只有行信息能建立打包前后的依赖关系。因此不管是开发环境或生产环境,我们都希望添加 cheap 的基本类型来忽略打包前后的列信息;
-
- module :不管是开发环境还是正式环境,我们都希望能定位到bug的源代码具体的位置,比如说某个 Vue 文件报错了,我们希望能定位到具体的 Vue 文件,因此我们也需要 module 配置;
-
- soure-map :source-map 会为每一个打包后的模块生成独立的 soucemap 文件 ,因此我们需要增加source-map 属性;
-
- eval-source-map:eval 打包代码的速度非常快,因为它不生成 map 文件,但是可以对 eval 组合使用 eval-source-map 使用会将 map 文件以 DataURL 的形式存在打包后的 js 文件中。在正式环境中不要使用 eval-source-map, 因为它会增加文件的大小,但是在开发环境中,可以试用下,因为他们打包的速度很快。
2.7、构建结果输出分析
Webpack 输出的代码可读性非常差而且文件非常大,让我们非常头疼。为了更简单、直观地分析输出结果,社区中出现了许多可视化分析工具。这些工具以图形的方式将结果更直观地展示出来,让我们快速了解问题所在。接下来讲解我们在 Vue 项目中用到的分析工具:webpack-bundle-analyzer 。
我们在项目中 webpack.prod.conf.js 进行配置:
- if (config.build.bundleAnalyzerReport) {
- var BundleAnalyzerPlugin = require(‘webpack-bundle-analyzer’).BundleAnalyzerPlugin;
- webpackConfig.plugins.push(new BundleAnalyzerPlugin());
- }
执行 $ npm run build --report 后生成分析报告如下:
2.8、Vue 项目的编译优化
如果你的 Vue 项目使用 Webpack 编译,需要你喝一杯咖啡的时间,那么也许你需要对项目的 Webpack 配置进行优化,提高 Webpack 的构建效率。具体如何进行 Vue 项目的 Webpack 构建优化,可以参考作者的另一篇文章《 Vue 项目 Webpack 优化实践》
三、基础的 Web 技术优化
3.1、开启 gzip 压缩
gzip 是 GNUzip 的缩写,最早用于 UNIX 系统的文件压缩。HTTP 协议上的 gzip 编码是一种用来改进 web 应用程序性能的技术,web 服务器和客户端(浏览器)必须共同支持 gzip。目前主流的浏览器,Chrome,firefox,IE等都支持该协议。常见的服务器如 Apache,Nginx,IIS 同样支持,gzip 压缩效率非常高,通常可以达到 70% 的压缩率,也就是说,如果你的网页有 30K,压缩之后就变成了 9K 左右
以下我们以服务端使用我们熟悉的 express 为例,开启 gzip 非常简单,相关步骤如下:
安装: npm install compression --save
- 添加代码逻辑:
-
- var compression = require(‘compression’);
- var app = express();
- app.use(compression())
重启服务,观察网络面板里面的 response header,如果看到如下红圈里的字段则表明 gzip 开启成功 :
3.2、浏览器缓存
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的,根据是否需要重新向服务器发起请求来分类,将 HTTP 缓存规则分为两大类(强制缓存,对比缓存),如果对缓存机制还不是了解很清楚的,可以参考作者写的关于 HTTP 缓存的文章《深入理解HTTP缓存机制及原理》,这里不再赘述。
3.3、CDN 的使用
浏览器从服务器上下载 CSS、js 和图片等文件时都要和服务器连接,而大部分服务器的带宽有限,如果超过限制,网页就半天反应不过来。而 CDN 可以通过不同的域名来加载文件,从而使下载文件的并发连接数大大增加,且CDN 具有更好的可用性,更低的网络延迟和丢包率 。
3.4、使用 Chrome Performance 查找性能瓶颈
Chrome 的 Performance 面板可以录制一段时间内的 js 执行细节及时间。使用 Chrome 开发者工具分析页面性能的步骤如下。
打开 Chrome 开发者工具,切换到 Performance 面板
点击 Record 开始录制
刷新页面或展开某个节点
点击 Stop 停止录制
END
风格指南
router 是不是hash 是否需要配置nginx , publicPath , 是不是要配置cdn
- 1.利用对象的引用关系来实现
- 2.父子组件之间的数据传递
- 3.使用.sync修饰符
- 1.使用vue的transition标签结合css样式完成动画
- 2.利用animate.css结合transition实现动画
- 3.利用 vue中的钩子函数实现动画
-
-
- 1、document.getElementById("id")
- 2、this.$refs.xx
-
- 还有一些特殊的,比如this.$root、this.$parent、this.$children
.trim .number .stop .prevent
- 两部分 一部分 数据->虚拟dom->dom, 另一部分 响应式数据
- 这两部分大大节省了开发者对数据变动转换到页面显示的操作,可以让开发者聚焦业务,聚焦数据的处理。
- 1.通过Gzip压缩
- 2.使用路由懒加载
- 3.利用webpack中的externals这个属性把打包后不需要打包的库文件都分离出去,减小项目打包后的大小
- 4.使用SSR渲染
如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。如果想要使添加的值做到响应式,应当使用$set()来添加对象。
一则语法糖,相当于v-bind:value="xxx" 和 @input,意思是绑定了一个value属性的值,子组件可对value属性监听,通过$emit('input', xxx)的方式给父组件通讯。自己实现v-model方式的组件也是这样的思路。
-
- delete:只是被删除数组成员变为 empty / undefined,其他元素键值不变
- Vue.delete:直接删了数组成员,并改变了数组的键值(对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限制)
-
- 组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue。
-
- 插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身。
- 从详情页返回列表页时, 要保存所有状态, 比如: 滚动条位置, 数据, 下拉数据等
- 当时想用keep-alive, 后来没用, 直接存储一些关键数据, 返回到router时重新加载了数据
- el: 把当前实例挂载在元素上
- template: 实例模版, 可以是.vue中的template, 也可以是template选项, 最终会编译成render函数
- render: 不需要通过编译的可执行函数
-
- template和render, 开发时各有优缺点, 不过在线上尽量不要有template
-
- render, 没有则去编译
- 编译vdom
- 对实例进行watch
- 插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制——一般有下面几种:
-
- 添加全局方法或者属性。如: vue-custom-element
- 添加全局资源:指令/过滤器/过渡等。如 vue-touch
- 通过全局混入来添加一些组件选项。如 vue-router
- 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如 vue-router
-
- 示例:安装 ElementUI
-
- 安装:yarn add element-ui
-
- 引入,在 main.js 中写入以下内容:
-
- import Vue from 'vue'
- import App from './App.vue'
- import ElementUI from 'element-ui';
- import 'element-ui/lib/theme-chalk/index.css';
-
- Vue.config.productionTip = false
- Vue.use(ElementUI);
- new Vue({
- render: h => h(App),
- }).$mount('#app')
-
- 在组件中使用:
-
- <template>
- <div>
- <Button>Button</Button>
- </div>
- </template>
-
- <script>
- import { Button } from 'element-ui';
-
- export default {
- components: {
- Button
- }
- };
- </script>
-
-
- v-for 的优先级更高
- 避免出现这种情况,如果实在需要,则在外嵌套template,在这一层进行v-if判断,然后在内部进行v-for循环,避免每次只有v-if只渲染很少一部分元素,也需要遍历同级的所有元素
- 无法监听时的方案:
- 数组:改变数组的值:this.$set()
- 改变数组长度:arr.splice()
- 对象:改变原有属性:Object.assign()
- 增加新属性:this.$set()
处理数据动态变化后,dom还未及时更新的问题。nexttick就可以获取到数据更新后最新的dom变化
- deep设置为true 就可以监听到对象的变化
-
- let vm=new Vue({
- el:"#first",
- data:{msg:{name:'北京'}},
- watch:{
-
- msg:{
- handler (newMsg,oldMsg){
- console.log(newMsg);
- },
- immediate:true,
- deep:true
- }
- }
- })
- mixins 就是混入。
-
- 一个混入对象可以包含任意组件选项。同一个生命周期,混入对象会比组件的先执行。
-
- //暴露两个mixins对象
- export const mixinsTest1 = {
- methods: {
- hello1() {
- console.log("hello1");
- }
- },
- created() {
- this.hello1();
- },
- }
-
-
- export const mixinsTest2 = {
- methods:{
- hello2(){
- console.log("hello2");
- }
- },
- created() {
- this.hello2();
- },
- }
-
- <template>
- <div>
- home
- </div>
- </template>
- <script>
- import {mixinsTest1,mixinsTest2} from '../util/test.js'
- export default {
- name: "Home",
- data () {
- return {
- };
- },
- created(){
- console.log("1212");
- },
- mixins:[mixinsTest2,mixinsTest1] // 先调用哪个mixins对象,就先执行哪个
- }
- </script>
- webpack:output.path
- vue-cli3: outputDir
建议在created里注册,在beforeDestory移出
- ‘为什么只能有且只有一个根元素’
-
- 于是我花了二十多分钟去找了一下答案......竟然没有找到答案....
-
- 好的现在我来说说我的理解,如果有不对的地方欢迎指出。
-
- 我觉得这个问题需要从两个方面来说起:
-
- 1.new Vue({el:'#app'})
-
- 2.单文件组件中,template下的元素div
-
- 一、
-
- 当我们实例化Vue的时候,填写一个el选项,来指定我们的SPA入口:
-
- let vm = new Vue({
- el:'#app'
- })
-
- 同时我们也会在body里面新增一个id为app的div
-
- <body>
- <div id='app'></div>
- </body>
-
- 这很好理解,就是为vue开启一个入口,那我们不妨来想想,如果我在body下这样
-
- <body>
- <div id='app1'></div>
- <div id='app2'></div>
- </body>
-
- Vue其实并不知道哪一个才是我们的入口,因为对于一个入口来讲,这个入口就是一个‘Vue类’,Vue需要把这个入口里面的所有东西拿来渲染,处理,最后再重新插入到dom中。
- 如果同时设置了多个入口,那么vue就不知道哪一个才是这个‘类’。
-
- 二、
-
- 当我们在webpack搭建的vue开发环境下,使用单文件组件时,你可能会这样:
-
- <template>
- <div class='component'></div>
- </template>
-
- 那这里为什么template下也必须有且只能有一个div呢?
-
- 这里我们要先看一看template这个标签,这个标签是HTML5出来的新标签,它有三个特性:
-
- 1.隐藏性:该标签不会显示在页面的任何地方,即便里面有多少内容,它永远都是隐藏的状态;
-
- 2.任意性:该标签可以写在页面的任何地方,甚至是head、body、sciprt标签内;
-
- 3.无效性:该标签里的任何HTML内容都是无效的,不会起任何作用;
-
- 但是呢,你可以通过innerHTML来获取到里面的内容。
-
- 知道了这个,我们再来看.vue的单文件组件。其实本质上,一个单文件组件,本质上(我认为)会被各种各样的loader处理成为.js文件(因为当你import一个单文件组件并打印出来的时候,是一个vue实例),通过template的任意性我们知道,template包裹的HTML可以写在任何地方,那么对于一个.vue来讲,这个template里面的内容就是会被vue处理为虚拟dom并渲染的内容,导致结果又回到了开始 :既然一个.vue单文件组件是一个vue实例,那么这个实例的入口在哪里?
-
- 如果在template下有多个div,那么该如何指定这个vue实例的根入口?
- 为了让组件能够正常的生成一个vue实例,那么这个div会被自然的处理成程序的入口。
-
- 通过这个‘根节点’,来递归遍历整个vue‘树’下的所有节点,并处理为vdom,最后再渲染成真正的HTML,插入在正确的位置
input标签v-model用lazy修饰之后,vue并不会立即监听input Value的改变,会在input失去焦点之后,才会触发input Value的改变
v-once 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
- 1.检查nginx配置,是否正确设置了资源映射条件;
- 2.检查vue.config.js中是否配置了publicPath,若有则检查是否和项目资源文件在服务器摆放位置一致。
- <template>
- <el-button v-on="$listeners" v-bind="$attrs" :loading="loading" @click="myClick">
- <slot></slot>
- </el-button>
- </template>
-
- <script>
- export default {
- name: 'mButton',
- inheritAttrs: false,
- props: {
- debounce: {
- type: [Boolean, Number]
- }
- },
- data() {
- return {
- timer: 0,
- loading: false
- }
- },
- methods: {
- myClick() {
- if (!this.debounce) return
- this.loading = true
- clearTimeout(this.timer)
- this.timer = setTimeout(
- () => {
- this.loading = false
- },
- typeof this.debounce === 'boolean' ? 500 : this.debounce
- )
- }
- }
- }
- </script>
子组件传递多个参数,父组件用展开运算符获取
- 那要看你怎么监听了, 比如
- @keyup.enter,
- 或者直接全局监听
npm 安装 然后再main.js 引入 最后 vue.use(插件名)
写过,随便说点组件的引入问题、注册问题、传值问题吧
keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能,由于是一个抽象组件,所以在页面渲染完毕后不会被渲染成一个DOM元素
对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。
- 一个是侦听属性,一个是计算属性
- 2.一个是为了应对复杂的逻辑计算,一个是对数据的变化作出反应
- 3.一个是只有当缓存改变时才执行,一个是只要从新渲染就会执行
- 4.一个有缓存,一个没有缓存
- 四个字: 性能优化,
-
- 简述: 让vue在更新数据的时候可以更有针对性的
<style scoped> </style>
看过 看不懂
bind inserted update componentUpdated unbind
- MVVM用视图模型代替了MVP中的展示器,视图模型和视图实现了双向绑定,当视图发生变化的时候视图模型也会发生改变,当视图模型变化的时候视图也随之变化。
-
-
- MVP用展示器代替了控制器,而展示器是可以直接更新视图,所以MVP中展示器可以处理视图的请求并递送到模型又可以根据模型的变化更新视图,实现了视图和模型的完全分离。
-
-
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
生命周期就是vue从开始创建到销毁的过程,分为四大步(创建,挂载,更新,销毁),每一步又分为两小步,如beforeCreate,created。beforeCreate前,也就是new Vue的时候会初始化事件和生命周期;beforeCreate和created之间会挂载Data,绑定事件;接下来会根据el挂载页面元素,如果没有设置el则生命周期结束,直到手动挂载;el挂载结束后,根据templete/outerHTML(el)渲染页面;在beforeMount前虚拟DOM已经创建完成;之后在mounted前,将vm.$el替换掉页面元素el;mounted将虚拟dom挂载到真实页面(此时页面已经全部渲染完成);之后发生数据变化时触发beforeUpdate和updated进行一些操作;最后主动调用销毁函数或者组件自动销毁时beforeDestroy,手动撤销监听事件,计时器等;destroyed时仅存在Dom节点,其他所有东西已自动销毁。这就是我所理解的vue的一个完整的生命周期;
- 父子Coms: 1/2/3 ..
- 兄弟Coms: 4/5
- 跨级Coms: 4/5/6/7
-
- props
- $emit/$on
- ( $parents/$children ) / $refs
- Vuex
- Bus
- ( provide/inject )
- ( $attrs/$listeners )
- 虚拟 dom 是相对于浏览器所渲染出来的真实 dom 的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom 树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。
-
- 这种方式相当消耗计算资源,因为每次查询 dom 几乎都需要遍历整颗 dom 树,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树,那么每次 dom 的更改就变成了 js 对象的属性的更改,这样一来就能查找 js 对象的属性变化要比查询 dom 树的性能开销小。
双向数据绑定个人理解就是存在data→view,view→data两条数据流的模式。其实可以简单的理解为change和bind的结合。目前双向数据绑定都是基于Object.defineProperty()重新定义get和set方法实现的。修改触发set方法赋值,获取触发get方法取值,并通过数据劫持发布信息.
-
- 1、vue是完整一套由官方维护的框架,核心库主要有由尤雨溪大神独自维护,而react是不要脸的书维护(很多库由社区维护),曾经一段时间很多人质疑vue的后续维护性,似乎这并不是问题。
- 2、vue上手简单,进阶式框架,白话说你可以学一点,就可以在你项目中去用一点,你不一定需要一次性学习整个vue才能去使用它,而react,恐怕如果你这样会面对项目束手无策。
- 3、语法上vue并不限制你必须es6+完全js形式编写页面,可以视图和js逻辑尽可能分离,减少很多人看不惯react-jsx的恶心嵌套,毕竟都是作为前端开发者,还是更习惯于html干净。
- 4、很多人说react适合大型项目,适合什么什么,vue轻量级,适合移动端中小型项目,其实我想说,说这话的人是心里根本没点逼数,vue完全可以应对复杂的大型应用,甚至于说如果你react学的不是很好,写出来的东西或根本不如vue写的,毕竟vue跟着官方文档撸就行,自有人帮你规范,而react比较懒散自由,可以自由发挥
- 5、vue在国内人气明显胜过react,这很大程度上得益于它的很多语法包括编程思维更符合国人思想
- 这个感觉和对vue的理解是差不多的题
- 优点:1. 数据驱动
- 2.模块化
- 3.轻量级
- 4.SPA
- 5. 版本3.0的界面化管理工具比较好使
- 6.vue易入门
- 缺点:1. 不支持低版本浏览器
- 1.mvvm框架
- 2.数据驱动
- 3.SPA
- 4.渐进式
- 事件修饰符.stop .prevent .capture .self .once .passive
- 表单修饰符.number .lazy .trim
- vue的数据劫持有两个缺点:
- 1、无法监听通过索引修改数组的值的变化
- 2、无法监听object也就是对象的值的变化
- 所以vue2.x中才会有$set属性的存在
-
- proxy是es6中推出的新api,可以弥补以上两个缺点,所以vue3.x版本用proxy替换object.defineproperty
- 这个……全局的theme属性然后做class判断或者加载不同的样式文件。一种是编译时换肤 一种是用户操作换肤。编译时换肤可以通过css in js相关技术修改css预处理器的变量 。用户操作换肤 只能内置一些styleb变量供用户选择了
直接甩已经有的项目给他 简单说下 vuex router ,和项目中常用操作 和注意事项 比如什么时候可以用箭头函数 什么时候不能用 等等 快速上手。。剩下的只能靠自己,刷官网。
- 看实际情况,一般在 created(或beforeRouter) 里面就可以,如果涉及到需要页面加载完成之后的话就用 mounted。
-
- 在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素
- 而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点,(此时document.getelementById 即可生效了)。
给 process.env 对象添加了一个属性 npm_config_report: "true",表示开启编译完成后的报告。
升级webpack4,支持多进程
- 可以通过指令去做
- Vue.directive('hasPermission', {
- bind(el, binding, vnode) {
- const permissions = vnode.context.$store.state.account.permissions
- if (binding.value === '') return
- const value = binding.value.split(',')
- let flag = true
- for (const v of value) {
- if (!permissions.includes(v)) {
- flag = false
- }
- }
- if (!flag) {
- if (!el.parentNode) {
- el.style.display = 'none'
- } else {
- el.parentNode.removeChild(el)
- }
- }
- }
- }
- views目录存放一级路由的组件,即视图组件
- Components目录存放组件
- Store存放vuex相关文件
- Router目录存放路由相关文件
- Untils目录存放工具js文件
- API目录存放封装好的与后端交互的逻辑
- Assets存放静态文件
vant,mint,uniapp
- 1、在webpack.base.conf.js新增externals配置,表示不需要打包的文件,然后在index.html中通过CDN引入
-
- externals: {
- "vue": "Vue",
- "vue-router": "VueRouter",
- "vuex": "Vuex",
- "element-ui": "ELEMENT",
- "BMap": "BMap"
- }
-
- 2、使用路由懒加载
- devServer中把所有的服务人员的地址代理都写进去,
- 然后动态更改接口的baseUrl,这样切换不同后端人员的时候不用重启
Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API;vue是单页面应用,使页面局部刷新,不用每次跳转页面都要请求所有数据和dom,这样大大加快了访问速度和提升用户体验。而且他的第三方ui库很多节省开发时间。
vue-devtools
start
一、生命周期
先贴两张图:
vue生命周期
小程序生命周期
相比之下,小程序的钩子函数要简单得多。
vue的钩子函数在跳转新页面时,钩子函数都会触发,但是小程序的钩子函数,页面不同的跳转方式,触发的钩子并不一样。onLoad:页面加载
一个页面只会调用一次,可以在 onLoad 中获取打开当前页面所调用的 query 参数。
onShow:页面显示
每次打开页面都会调用一次。
onReady:页面初次渲染完成
一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
对界面的设置如 wx.setNavigationBarTitle请在 onReady之后设置。详见生命周期。
onHide:页面隐藏
当 navigateTo或底部tab切换时调用。
onUnload:页面卸载
当 redirectTo或 navigateBack的时候调用。
数据请求
在页面加载请求数据时,两者钩子的使用有些类似,vue一般会在 created或者 mounted中请求数据,而在小程序,会在 onLoad或者 onShow中请求数据。
二、数据绑定
vue:vue动态绑定一个变量的值为元素的某个属性的时候,会在变量前面加上冒号:,例:
小程序:绑定某个变量的值为元素属性时,会用两个大括号括起来,如果不加括号,为被认为是字符串。例:
三、列表渲染
直接贴代码,两者还是有些相似:
vue:
小程序:
四、显示与隐藏元素
vue中,使用 v-if 和 v-show控制元素的显示和隐藏。
小程序中,使用 wx-if和 hidden控制元素的显示和隐藏。
五、事件处理
vue:使用 v-on:event绑定事件,或者使用 @event绑定事件,例如:
小程序中,全用 bindtap(bind+event),或者 catchtap(catch+event)绑定事件,例如:
六、数据双向绑定
1、设置值
在vue中,只需要再表单元素上加上 v-model,然后再绑定 data中对应的一个值,当表单元素内容发生变化时, data中对应的值也会相应改变,这是vue非常nice的一点。
但是在小程序中,却没有这个功能。那怎么办呢?
当表单内容发生变化时,会触发表单元素上绑定的方法,然后在该方法中,通过 this.setData({key:value})来将表单上的值赋值给 data中的对应值。
下面是代码,可以感受一下:
当页面表单元素很多的时候,更改值就是一件体力活了。和小程序一比较,vue的 v-model简直爽的不要不要的。
2、取值
vue中,通过 this.reason取值。
小程序中,通过 this.data.reason取值。
七、绑定事件传参
在vue中,绑定事件传参挺简单,只需要在触发事件的方法中,把需要传递的数据作为形参传入就可以了,例如:
在 小程序中,不能直接在绑定事件的方法中传入参数,需要将参数作为属性值,绑定到元素上的 data-属性上,然后在方法中,通过 e.currentTarget.dataset.*的方式获取,从而完成参数的传递,很麻烦有没有…
八、父子组件通信
1、子组件的使用
在vue中,需要:
1、编写子组件
2、在需要使用的父组件中通过 import引入
3、在 vue的 components中注册
4、在模板中使用
在小程序中,需要:
1、编写子组件
2、在子组件的 json文件中,将该文件声明为组件
3、在需要引入的父组件的 json文件中,在 usingComponents填写引入组件的组件名以及路径
4、在父组件中,直接引入即可
具体代码:
2、父子组件间通信
在vue中
父组件向子组件传递数据,只需要在子组件通过 v-bind传入一个值,在子组件中,通过 props接收,即可完成数据的传递,示例:
子组件和父组件通信可以通过 this.$emit将方法和数据传递给父组件。
在小程序中
父组件向子组件通信和vue类似,但是小程序没有通过 v-bind,而是直接将值赋值给一个变量,如下:
此处, “index”就是要向子组件传递的值。
在子组件 properties中,接收传递的值。
子组件向父组件通信和 vue也很类似,代码如下:
如果父组件想要调用子组件的方法
vue会给子组件添加一个 ref属性,通过 this.$refs.ref的值便可以获取到该子组件,然后便可以调用子组件中的任意方法,例如:
小程序是给子组件添加 id或者 class,然后通过 this.selectComponent找到子组件,然后再调用子组件的方法,示例:
小程序和vue在这点上太相似了,有木有。。
END
- keep-alive
- 通过actived钩子
start
高阶组件
高阶组件介绍
vue 高阶组件的认识,在React中组件是以复用代码实现的,而Vue中是以mixins 实现,并且官方文档中也缺少一些高阶组件的概念,因为在vue中实现高阶组很困难,并不像React简单,其实vue中mixins也同样和以代替,在读了一部分源码之后,对vue有了更深的认识
所谓高阶组件其实就是一个高阶函数, 即返回一个组件函数的函数,Vue中怎么实现呢? 注意 高阶组件有如下特点
- 高阶组件(HOC)应该是无副作用的纯函数,且不应该修改原组件,即原组件不能有变动
- 高阶组件(HOC)不关心你传递的数据(props)是什么,并且新生成组件不关心数据来源
- 高阶组件(HOC)接收到的 props 应该透传给被包装组件即直接将原组件prop传给包装组件
- 高阶组件完全可以添加、删除、修改 props
高阶组件举例
Base.vue
- <template>
- <div>
- <p @click="Click">props: {{test}}</p>
- </div>
- </template>
- <script>
- export default {
- name: 'Base',
- props: {
- test: Number
- },
- methods: {
- Click () {
- this.$emit('Base-click')
- }
- }
- }
- </script>
Vue 组件主要就是三点:props、event 以及 slots。对于 Base组件 组件而言,它接收一个数字类型的 props 即 test,并触发一个自定义事件,事件的名称是:Base-click,没有 slots。我们会这样使用该组件:
现在我们需要 base-component 组件每次挂载完成的时候都打印一句话:haha,同时这也许是很多组件的需求,所以按照 mixins 的方式,我们可以这样做,首先定义个 mixins
- export default consoleMixin {
- mounted () {
- console.log('haha')
- }
- }
然后在 Base 组件中将 consoleMixin 混入:
- <template>
- <div>
- <p @click="Click">props: {{test}}</p>
- </div>
- </template>
- <script>
- export default {
- name: 'Base',
- props: {
- test: Number
- },
- mixins: [ consoleMixin ],
- methods: {
- Click () {
- this.$emit('Base-click')
- }
- }
- }
- </script>
这样使用 Base 组件的时候,每次挂载完成之后都会打印一句 haha,不过现在我们要使用高阶组件的方式实现同样的功能,回忆高阶组件的定义:接收一个组件作为参数,返回一个新的组件,那么此时我们需要思考的是,在 Vue 中组件是什么?Vue 中组件是函数,不过那是最终结果,比如我们在单文件组件中的组件定义其实就是一个普通的选项对象,如下:
- export default {
- name: 'Base',
- props: {...},
- mixins: [...]
- methods: {...}
- }
这难道不是一个纯对象嘛
- import Base from './Base.vue'
- console.log(Base)
这里的Base是什么呢 对就是一个JSON对象,而当以把他加入到一个组件的components,Vu最终会以该参数即option来构造实例的构造函数,所以Vue中组件就是个函数,但是在引入之前仍只是一个options对象,所以这样就很好明白了 Vue中组件开始只是一个对象,即高阶组件就是 一个函数接受一个纯对象,并且返回一个新纯对象
- export default function Console (BaseComponent) {
- return {
- template: '<wrapped v-on="$listeners" v-bind="$attrs"/>',
- components: {
- wrapped: BaseComponent
- },
- mounted () {
- console.log('haha')
- }
- }
- }
这里 Console就是一个高阶组件,它接受一个参数 BaseComponent即传入的组件,返回一个新组件,将BaseComponent作为新组件的子组件并且在mounted里设置钩子函数 打印haha,我们可以完成mixins同样做到的事,我们并没有修改子组件Base,这里的 $listeners $attrs 其实是在透传props 和事件 那这样真的就完美解决问题了吗?不是的,首先 template 选项只有在完整版的 Vue 中可以使用,在运行时版本中是不能使用的,所以最起码我们应该使用渲染函数(render)替代模板(template)
Console.js
- export default function Console (BaseComponent) {
- return {
- mounted () {
- console.log('haha')
- },
- render (h) {
- return h(BaseComponent, {
- on: this.$listeners,
- attrs: this.$attrs,
- })
- }
- }
- }
我们将模板改写成了渲染函数,看上去没什么问题,实际还是有问题,上面的代码中 BaseComponent 组件依然收不到 props,为什么呢,我们不是已经在 h 函数的第二个参数中将 attrs 传递过去了吗,怎么还收不到?当然收不到,attrs 指的是那些没有被声明为 props 的属性,所以在渲染函数中还需要添加 props 参数:
- export default function Console (BaseComponent) {
- return {
- mounted () {
- console.log('haha')
- },
- render (h) {
- return h(BaseComponent, {
- on: this.$listeners,
- attrs: this.$attrs,
- props: this.$props
- })
- }
- }
- }
那这样呢 其实还是不行 props始终是空对象,这里的props是高阶组件的对象,但是高阶组件并没有声明props所以如此故要再声明一个props
- export default function Console (BaseComponent) {
- return {
- mounted () {
- console.log('haha')
- },
- props: BaseComponent.props,
- render (h) {
- return h(BaseComponent, {
- on: this.$listeners,
- attrs: this.$attrs,
- props: this.$props
- })
- }
- }
- }
ok 一个差不多的高阶组件就完成了 但是能还每完 我们只实现了 透传props,透传事件,emmmm就剩下slot了 我们修改 Base 组件为其添加一个具名插槽和默认插槽 Base.vue
- <template>
- <div>
- <span @click="handleClick">props: {{test}}</span>
- <slot name="slot1"/> <!-- 具名插槽 --></slot>
- <p>===========</p>
- <slot><slot/> <!-- 默认插槽 -->
- </div>
- </template>
-
- <script>
- export default {
- ...
- }
- </script>
-
- <template>
- <div>
- <Base>
- <h2 slot="slot1">BaseComponent slot</h2>
- <p>default slot</p>
- </Base>
- <wrapBase>
- <h2 slot="slot1">EnhancedComponent slot</h2>
- <p>default slot</p>
- </wrapBase>
- </div>
- </template>
-
- <script>
- import Base from './Base.vue'
- import hoc from './Console.js'
-
- const wrapBase = Console(Base)
-
- export default {
- components: {
- Base,
- wrapBase
- }
- }
- </script>
这里的执行结果就是 wrapBase里的slot都没有了 所以就要改一下高阶组建了
- function Console (BaseComponent) {
- return {
- mounted () {
- console.log('haha')
- },
- props: BaseComponent.props,
- render (h) {
-
- // 将 this.$slots 格式化为数组,因为 h 函数第三个参数是子节点,是一个数组
- const slots = Object.keys(this.$slots)
- .reduce((arr, key) => arr.concat(this.$slots[key]), [])
-
- return h(BaseComponent, {
- on: this.$listeners,
- attrs: this.$attrs,
- props: this.$props
- }, slots) // 将 slots 作为 h 函数的第三个参数
- }
- }
- }
这时 slot内容确实渲染出来了 但是顺序不太对 高阶组件的全部渲染到了末尾。。 其实 Vue在处理具名插槽会考虑作用域的因素 首先 Vue 会把模板(template)编译成渲染函数(render),比如如下模板:
- <div>
- <p slot="slot1">Base slot</p>
- </div>
会被编译成如下渲染函数:
- var render = function() {
- var _vm = this
- var _h = _vm.$createElement
- var _c = _vm._self._c || _h
- return _c("div", [
- _c("div", {
- attrs: { slot: "slot1" },
- slot: "slot1"
- }, [
- _vm._v("Base slot")
- ])
- ])
- }
观察上面的渲染函数我们发现普通的 DOM 是通过 _c 函数创建对应的 VNode 的。现在我们修改模板,模板中除了有普通 DOM 之外,还有组件,如下:
- <div>
- <Base>
- <p slot="slot1">Base slot</p>
- <p>default slot</p>
- </Base>
- </div>
其render函数
- var render = function() {
- var _vm = this
- var _h = _vm.$createElement
- var _c = _vm._self._c || _h
- return _c(
- "div",
- [
- _c("Base", [
- _c("p", { attrs: { slot: "slot1" }, slot: "slot1" }, [
- _vm._v("Base slot")
- ]),
- _vm._v(" "),
- _c("p", [_vm._v("default slot")])
- ])
- ],
- )
- }
我们发现无论是普通DOM还是组件,都是通过 _c 函数创建其对应的 VNode 的 其实 _c 在 Vue 内部就是 createElement 函数。createElement 函数会自动检测第一个参数是不是普通DOM标签如果不是普通DOM标签那么 createElement 会将其视为组件,并且创建组件实例,注意组件实例是这个时候才创建的 但是创建组件实例的过程中就面临一个问题:组件需要知道父级模板中是否传递了 slot 以及传递了多少,传递的是具名的还是不具名的等等。那么子组件如何才能得知这些信息呢?很简单,假如组件的模板如下
- <div>
- <Base>
- <p slot="slot1">Base slot</p>
- <p>default slot</p>
- </Base>
- </div>
父组件的模板最终会生成父组件对应的 VNode,所以以上模板对应的 VNode 全部由父组件所有,那么在创建子组件实例的时候能否通过获取父组件的 VNode 进而拿到 slot 的内容呢?即通过父组件将下面这段模板对应的 VNode 拿到
- <Base>
- <p slot="slot1">Base slot</p>
- <p>default slot</p>
- </Base>
如果能够通过父级拿到这段模板对应的 VNode,那么子组件就知道要渲染哪些 slot 了,其实 Vue 内部就是这么干的,实际上你可以通过访问子组件的 this.$vnode 来获取这段模板对应的 VNode
this.$vnode 并没有写进 Vue 的官方文档
子组件拿到了需要渲染的 slot 之后进入到了关键的一步,这一步就是导致高阶组件中透传 slot 给 Base组件 却无法正确渲染的原因 children的VNode中的context引用父组件实例 其本身的context也会引用本身实例 其实是一个东西
console.log(this.v n o d e . c o n t e x t = = = t h i s . vnode.context === this.vnode.context===this.vnode.componentOptions.children[0].context) //ture
而 Vue 内部做了一件很重要的事儿,即上面那个表达式必须成立,才能够正确处理具名 slot,否则即使 slot 具名也不会被考虑,而是被作为默认插槽。这就是高阶组件中不能正确渲染 slot 的原因
即 高阶组件中 本来时父组件和子组件之间插入了一个组件(高阶组件),而子组件的 this.$vnode其实是高阶组件的实例,但是我们将slot透传给子组件,slot里 VNode 的context实际引用的还是父组件 所以
console.log(this.v n o d e . c o n t e x t = = = t h i s . vnode.context === this.vnode.context===this.vnode.componentOptions.children[0].context) // false
最终导致具名插槽被作为默认插槽,从而渲染不正确。
决办法也很简单,只需要手动设置一下 slot 中 VNode 的 context 值为高阶组件实例即可
- function Console (Base) {
- return {
- mounted () {
- console.log('haha')
- },
- props: Base.props,
- render (h) {
- const slots = Object.keys(this.$slots)
- .reduce((arr, key) => arr.concat(this.$slots[key]), [])
- // 手动更正 context
- .map(vnode => {
- vnode.context = this._self //绑定到高阶组件上
- return vnode
- })
-
- return h(WrappedComponent, {
- on: this.$listeners,
- props: this.$props,
- attrs: this.$attrs
- }, slots)
- }
- }
- }
说明白就是强制把slot的归属权给高阶组件 而不是 父组件 通过当前实例 _self 属性访问当实例本身,而不是直接使用 this,因为 this 是一个代理对象
END
也可以写为js,jsx,ts,tsx这种
解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理。
- extend的作用是继承当前的Vue类,传入一个extendOption生成一个新的构造函数。在extend的时候会进行mergeOption,融合Vue原型上的baseOption,所以extend出来的子类也能使用v-model、keep-alive等全局性的组件。
-
- 作用是生成组件类。在挂载全局组件和设置了components属性的时候会使用到。在生成DOM的时候会new 实例化挂载。
- // 统一处理axios请求
- async getHistoryData (data) {
- try {
- let res = await axios.get('/api/survey/list/', {
- params: data
- })
- this.tableData = res.data.result
- this.totalData = res.data.count
- } catch (err) {
- console.log(err)
- alert('请求出错!')
- }
- }
- }
errorHandler
虚拟DOM本身是一个JavaScript对象模拟真实DOM ,用对象的属性去描述一个DOM节点,最终也只是一个真实DOM的映射
- 1、字符串
- 2、模板字面量
- 3、<script type="x-template"></script>
- 4、文件组件模板
- 5、inline-template
- 1.监听地址栏中hash变化驱动界面变化
-
- 2.用pushsate记录浏览器的历史,驱动界面发送变化
-
- 3.直接在界面用普通事件驱动界面变化
-
- 它们都是遵循同一种原则:div 的显示与隐藏
- 介绍:SPA应用就是一个web应用,可理解为:是一种只需要将单个页面加载到服务器之中的web应用程序。当浏览器向服务器发出第一个请求时,服务器会返回一个index.html文件,它所需的js,css等会在显示时统一加载,部分页面需要时加载。
- 优点:
- 1.良好的交互式体验。意思是:用户无需刷新页面,获取数据通过异步ajax获取,页面显示流畅
- 2.良好的前后端分离模式(MVVM),减轻服务端压力。服务器只需要输出数据就可以,不用管逻辑和页面展示,吞吐能力会提高几倍
- 3.共用同一套后端程序代码,不用修改就可用于web界面,手机和平板等客户端设备
- 缺点:
- 1.不利于SEO优化
- 2.由于单页应用在一个页面中显示,所以不可以使用浏览器自带的前进后退功能,想要实现页面切换需要自己进行管理
- 3.首屏加载过慢(初次加载耗时多),原因是:为了实现单页web应用功能及展示效果,在页面初始化的时候就会将js,css等统一加载,部分页面在需要时加载。当然也有解决方法。
- 解决方法:①使用路由懒加载 ②开启Gzip压缩 ③使用webpack的externals属性把不需要的库文件分离出去,减少打包后文件的大小 ④使用vue的服务端渲染(SSR)
- 举例spa应用:网易云音乐、QQ音乐等
mounted生命周期
beforeCreate, created, beforeMount, mounted
-
- beforeCreate:在 new 一个 vue 实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。
- created:data 和 methods 都已经被初始化好了。(如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作)
- beforeMount:在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的。
- mounted:Vue 实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 (如果我们想要通过插件操作页面上的 DOM 节点,最早可以在和这个阶段中进行)
- beforeUpdate:页面中的显示的数据还是旧的,data 中的数据是更新后的, 页面还没有和最新的数据保持同步。
- updated:页面显示的数据和 data 中的数据已经保持同步了,都是最新的。
- beforeDestroy:Vue 实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁。
- destroyed:这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于不可用状态。组件已经被销毁了。
-
准确地控制数据流和其对DOM的影响
Vue.js - 多样化的 JavaScript 框架
作为一个跨平台的,高度进步的框架,Vue 成为了许多需要创建单页应用程序的开发人员的首选。在用于开发 Web 应用程序的典型 MVC 体系结构中,Vue 充当了 View,这意味着它可以让开发者看到数据的显示部分。除了上面提到的基本功能之外,Vue 还有许多其它优秀功能。
我们来看看这些:
容易使用
如果你一直在使用其它框架,那么你可以轻松使用 Vue,因为 Vue 的核心库专注于 View 层,你可以轻松地将其与第三方库进行整合并与现有项目一起使用。
2. 轻便
由于 Vue 主要关注于 ViewModel 或双向数据绑定,因此 Vue 很轻便。Vue 也具有十分基础的文档。Vue 用做 View 层,意味着开发者可以将它用作页面中的亮点功能,比起全面的 SPA,Vue 提供了更好的选择。
3. 学习曲线很低
熟悉 HTML 的开发人员会发现 Vue 的学习曲线很低,同时对于经验较少的开发人员和初学者来说,也能够快速地学习和理解 Vue。
4. 双向绑定
Vue 提供了 v-model 指令(用于更新用户输入事件的数据),使得在表单输入和结构元素上实现双向绑定变得很容易。它可以选择正确的方式来更新输入类型相关的元素。
5. 虚拟 DOM
由于 Vue 是基于 Snabbdom 的轻量级虚拟 DOM 实现,因此 Vue 的性能有些许的提升。这是虚拟 DOM 的主要新功能之一,开发者可以直接进行更新。当你需要在实际的 DOM 中进行更改时,只需执行一次这样的更新功能。
6. 基于 HTML 模板的语法
Vue 允许开发者直接将渲染的 DOM 绑定到底层的Vue实例数据上。这是一个很有用的功能,因为它可以让开发者扩展基本的 HTML 元素,来保存可复用的代码。
Angular:动态框架
Angular 是一个功能齐全的框架,支持 Model-View-Controller 编程结构,非常适合构建动态的单页网络应用程序。
谷歌在2009年开发出了 Angular 并对其提供支持,Angular 包含一个基于标准 JavaScript 和 HTML 的 JS 代码库。Angular 设计的最初目的是作为一个使设计者能够与后端和前端进行交互的工具。
以下是 Angular 的部分最好的功能:
Model-View-ViewModel(MVVM)
为了构建客户端Web应用程序,Angular 将原始 MVC 软件设计模式背后的基本原理结合在一起。然而,Angular 没有实现传统意义上的 MVC,而是实现了 MVVM 即 Model-View-ViewModel 模式。
2. 依赖注入
Angular 带有内置的依赖注入子系统功能,这使得应用程序易于开发和测试。依赖注入允许开发者通过请求来获得依赖关系,而不是搜索依赖关系。这对开发人员非常有帮助。
3. 测试
在 Angular 中,可以单独对控制器和指令进行单元测试。Angular 允许开发人员进行端到端和单元测试运行器设置,这意味着也可以从用户角度进行测试。
4. 跨浏览器兼容
Angular 的一个有趣功能是,框架中编写的应用程序在多个浏览器都能运行良好。Angular 可以自动处理每个浏览器所需的代码。
5. 指令
Angular 的指令(用于渲染指令的DOM模板) 可用于创建自定义的 HTML 标记。这些是 DOM 元素上的标记,因为开发者可以扩展指令词汇表并制作自己的指令,或将它们转换为可重用组件。
6. Deep Linking
由于 Angular 主要用于制作单页应用程序,因此必须利用 Deep Linking 功能才能在同一页面上加载子模板。Deep Linking 的目的是为了查看位置 URL 并安排它映射到页面的当前状态。
Deep Linking 功能通过查看页面状态并将用户带到特定内容,而不是从主页中遍历应用程序来设置 URL。Deep Linking 允许所有主要搜索引擎,可以轻松的搜索网络应用程序。
Vue.js 与 Angular–哪一个最好?
究竟哪个框架是最好的 - Angular 还是 Vue?下面我们通过以下几点来深入研究:
学习曲线
在学习曲线方面,Vue.js 的学习和理解相对简单,而 Angular 则需要时间去习惯。开发人员认为这两个框架对于项目来说都很棒,但开发者中的大多数人更喜欢使用 Vue,因为当将 Vuex 添加到项目中时,Vue 更加简单并且可以很好地扩展 。
尽管 Vue 和 Angular 的一些语法类似,比如 API 和设计(这是因为 Vue 实际上是从 Angular 的早期开发阶段中获得启发的),但 Vue 一直致力于在一些对于 Angular 来说很困难的方面提升自己 。开发者可以在几个小时内用 Vue.js 构建一个特别的应用程序,但是这对 Angular 来说是不可能的。
灵活性
Angular 是独立的,这意味着你的应用程序应该有一定的构造方式。Vue 则更加宽泛,Vue 为创建应用程序提供了模块化,灵活的解决方案。
很多时候,Vue 被认为是一个库而不是框架。默认情况下,Vue 不包含路由器,HTTP 请求服务等。开发者必须安装所需的“插件”。Vue 非常灵活并且可以与大多数开发者想要使用的库兼容。
不过,也有开发人员更喜欢 Angular,因为 Angular 为其应用程序的整体结构提供了支持。这有助于节省编码时间。
文档对象模型(DOM)
Vue 通过最少量的组件重新渲染,可以将模板预编译为纯 JavaScript。这个虚拟 DOM 允许进行大量的优化,这是 Vue 和 Angular 之间的主要区别。Vue 允许使用更简单的编程模型,而 Angular 则以跨浏览器兼容的方式操作 DOM。
速度/性能
虽然 Angular 和 Vue 都提供了很高的性能,但由于 Vue 的虚拟 DOM 实现的重量较轻,所以可以说 Vue 的速度/性能略微领先。
更简单的编程模型使 Vue 能够提供更好的性能。Vue 可以在没有构建系统的情况下使用,因为开发者可以将其包含在 HTML 文件中。这使得 Vue 易于使用,从而提高了性能。
Angular 可能会很慢的原因是它使用脏数据检查,这意味着 Angularmonitors 会持续查看变量是否有变化。
双向数据绑定
这两个框架均支持双向数据绑定,但与 Vue.js 相比,Angular 的双向绑定更加复杂。Vue 中的双向数据绑定非常简单,而在 Angular 中,数据绑定更加简单。
何时选择 Vue.js?
如果你希望通过以最简单的方式来制作 Web 应用程序,那么你应该选择 Vue。如果你的 Javascript 基础不是太强大,或者有严格的开发截止日期,Vue 将是一个很好的选择。
如果你的前端是 Laravel,那么请选择 Vue。Laravel 社区的开发者认为 Vue 是他们最喜欢的框架。Vue 将总处理时间缩短了50%,并释放了服务器上的空间。
如果是开发小规模应用或者开发时不喜欢受约束,请选择Vue。
如果你很熟悉 ES5 Javascript 和 HTML,那么请使用 Vue 完成你的项目。
如果你想要在浏览器中编译模板并且使用其简单性,使用独立版本的Vue会很好。
如果你打算构建性能关键型SPA或需要功能范围的 CSS,Vue 的单文件组件会非常完美。
何时选择 Angular?
如果你需要构建大型复杂的应用程序,那么应该选择 Angular,因为 Angular 为客户端应用程序开发提供了一个完整而全面的解决方案。
对于希望处理客户端和服务器端模式的开发人员来说,Angular 是一个不错的选择。开发人员喜欢 Angular 的主要原因是 Angular 能够使他们专注于任何类型的设计,无论是 jQuery 调用还是 DOM 配置干扰。
对于开发人员创建具有多个组件和复杂需求的 Web 应用程序,Angular 也同样适用。当你选择Angular 时,本地开发人员会发现更容易理解应用程序功能和编码结构。
如果你想在新项目中选择现有组件,也可以选择 Angular,因为你只需复制和粘贴代码即可。
Angular 可以使用双向数据绑定功能来管理 DOM 和模型之间的同步。这使 Angular 成为了 Web 应用程序开发的强有力的工具。
希望制作更轻更快的Web应用程序的开发人员可以利用 Angular 中的 MVC 结构和独立的逻辑和数据组件,这有助于加速开发过程。
代码比较
分析 Vue 和 Angular 的代码很有趣。包含标记,样式和行为的代码可以帮助开发者构建高效且可重用的接口。在 Angular 中,控制器和指令等实体包含在模块中,而 Vue 的模块中包含组件逻辑。
Vue 组件
- Vue.extend({
- data: function(){ return {…} },
- created: function() {…},
- ready: function() {…},
- components: {…},
- methods: {…},
- watch: {…}
- //(other props excluded)
- });
Angular 模块
angular.module(‘myModule’, […]);
Angular 中的 Directive 更加强大。
Vue 指令
- Vue.directive(‘my-directive’, {
- bind: function () {…},
- update: function (newValue, oldValue) {…},
- unbind: function () {…}
- });
Angular 指令
- myModule.directive(‘directiveName’, function (injectables) {
- return {
- restrict: ‘A’,
- template: ‘
-
- ’,
- controller: function() { … },
- compile: function() {…},
- link: function() { … }
- //(other props excluded) };
- });
由于 Vue.js 受 Angular 启发,也借用了 Angular 的模板语法。因此循环,这两个框架的插值和条件的语法都非常相似。
下面给出代码片段:
Vue 插值
{{myVariable}}
角插值
{{myVariable}}
Vue 循环
{{myProperty}}
Angular 循环
{{item.myProperty}}
Vue 条件
角度条件
Vue.js 的编码使得页面渲染变得非常简单。事实上,Vue.js 更像是一个库而不是框架,因为它不提供 Angular 的所有功能。开发者将不得不依赖 Vue.js 的第三方代码,而 Angular 提供了 HTTP 请求服务或路由器等功能。
如何引入scss?引入后如何使用?
安装scss依赖包:
npm install sass-loader --save-dev npm install node-sass --save-dev
在build文件夹下修改 webpack.base.conf.js 文件:
在 module 下的 rules 里添加配置,如下:
{ test: /\.scss$/, loaders: ['style', 'css', 'sass'] }
应用:
在vue文件中应用scss时,需要在style样式标签上添加lang="scss",即<style lang="scss">。
- 创建一个request.js用于封装axios,在 src/api/request,设置拦截器统一处理请求和相应。
-
- 封装 axios:request.js:
-
- import axios from 'axios'
- import {Message, Loading} from "element-ui"
- import {getToken} from "@/utils/auth"
-
- function Index({...config}) {
- // create an axios instance
- const service = axios.create({
- /*headers: {
- 'Cache-Control': 'no-cache'
- },*/
- baseURL: config.baseURL || process.env.VUE_APP_BASE_API, // url = base url + request url
- // withCredentials: true, // send cookies when cross-domain requests
- timeout: 30000 // request timeout
- })
-
- // request interceptor
- service.interceptors.request.use(
- config => {
- return config
- },
- error => {
- return Promise.reject(error)
- }
- )
-
- // response interceptor
- service.interceptors.response.use(
- response => {
- return response
- },
- error => {
- const {request = {}} = error;
- const {status, response} = request;
-
- error.status = status
- try {
- error.res = JSON.parse(response)
- } catch (e) {
- console.warn(e)
- }
- return Promise.reject(error)
- }
- )
-
- /**
- * 发起请求
- * @param method 请求方法
- * @param url 请求地址
- * @param params 要发送的数据
- * @param config 配置
- * @param axiosConfig Axios配置项
- * @returns {Promise<never>|Promise<AxiosResponse<T>>}
- */
- const requestProcessor = (method, url, params, config, axiosConfig) => {
- const headers = {}
- const token = getToken().token
- if (token) {
- // let each request carry token
- headers['Authorization'] = 'JWT ' + token
- }
-
- if (config.formData) {
- const fd = new FormData();
- for (let key in params) {
- fd.append(key, params[key])
- }
- params = fd
- }
-
- switch (method.toUpperCase()) {
- case 'GET':
- return service.get(url, {
- params,
- headers,
- ...axiosConfig,
- })
- case 'POST':
- return service.post(url, params, {
- headers,
- ...axiosConfig,
- })
- case 'DELETE':
- return service.delete(url, {
- params,
- headers,
- ...axiosConfig,
- })
- case 'PUT':
- return service.put(url, params, {
- headers,
- ...axiosConfig,
- })
- default:
- return Promise.reject(new Error(`${method} 方法无效,请用正确的请求方法`))
- }
- }
-
- this.service = async ({method, url, params, config = {}, axiosConfig = {}}) => {
- const {isLoading = true, isToast = true} = config
-
- let loadingInstance
- isLoading && (loadingInstance = Loading.service({
- fullscreen: true,
- background: 'transparent',
- text: '加载中...'
- }))
-
- try {
- const response = await requestProcessor(method, url, params, config, axiosConfig)
- // 此处可以再次拦截
- return response.data
- } catch (error) {
- isToast && Message.error(error.message)
- throw error
- } finally {
- isLoading && loadingInstance.close()
- }
-
- }
- }
-
- export const {request} = new Index()
- export default Index
-
- 接口 listing.js:
-
- import Request from "@/api/request"
-
- const {service} = new Request()
-
- export default {
- userPostList({pageSize, page}) {
- return service({
- method: 'get',
- url: '/userpostlist/',
- params: {
- pageSize,
- page
- },
- config: {
- isLoading: false
- }
- })
- }
- }
-
- 在 Vue 组件中使用:
-
- import listing from "@/api/listing"
-
- export default {
- mounted() {
- this.getList()
- },
- methods: {
- getList() {
- this.isLoading = true
-
- listing.userPostList({
- pageSize: this.pageSize,
- page: this.currentPage,
- }).then(data => {
- this.currentPage = parseInt(data.currentPage)
- this.total = data.total
- this.list = data.results
-
- }).finally(() => {
- this.isLoading = false
- })
- }
- }
- }
- 1.vue-resources不再更新了,vue作者尤大推荐axios。
- 2.axios更加强大
- 3.axios就是一个基于ES6的Promise的网络请求库,其实说干净了就是一个打包好的XMLHttpRequests,也就是说,这个也是一个ajax库。
- 4.axios
- 在浏览器里建立XHR
- 通过nodejs进行http请求
- 转换或者拦截请求数据或响应数据
- 支持Promise的API
- 可以取消请求
- 自动转换JSON
- 可以防御XSRF攻击!
- 5.vue-resources
- 只提供了浏览器版本
-
- 这是官方的回答;我的回答是大势所趋.
- 1.axios通过对Promise的封装实现异步请求;
- 2.
- if(answer == '有'){
- if(这个问题到此为止){
- return ‘有’;
- }else{
- return '没';
- }
- }
axios 的是一种异步请求,用法和ajax类似,安装npm install axios --save 即可使用,请求中包括get,post,put, patch ,delete等五种请求方式,解决跨域可以在请求头中添加Access-Control-Allow-Origin,也可以在index.js文件中更改proxyTable配置等解决跨域问题
将template里的组件编译成虚拟dom
<input type="text" :value="name" @input="onInput" @focus="onFocus" @blur="onBlur" />
- 不对原组件进行更改的:
-
- 使用Vue.extend直接扩展
- 使用Vue.mixin全局混入
- HOC封装
- 报错 变量未定义
- 以 _ 或 $ 开头的属性 不会 被 Vue 实例代理,因为它们可能和 Vue 内置的属性、API 方法冲突。
- 你可以使用例如 $data.xxx或者_data.xxx 的方式访问这些属性。
键名优先级:props > data > methods
- 两种方式
- 1、组件外部加修饰符.navtive
- 2、组件内部声明$emit('自定义事件')
- .babelrc 是目前 babel-polyfill 的最佳实践
- {
- "presets": [
- [
- "@babel/preset-env",
- {
- "corejs": "3",
- "modules": false,
- "useBuiltIns": "usage"
- }
- ]
- ],
- "plugins": [
- [
- "@babel/plugin-transform-runtime",
- {
- "corejs": false,
- "helpers": true,
- "regenerator": false,
- "useESModules": true
- }
- ]
- ]
- }
-
- 强制重新渲染
-
- this.$forceUpdate()
-
- 强制重新刷新某组件
-
- //模版上绑定key
- <SomeComponent :key="theKey"/>
- //选项里绑定data
- data(){
- return{
- theKey:0
- }
- }
- //刷新key达到刷新组件的目的
- theKey++;
style 不是必须的,script 是必须的,而且必须要写上
event.currentTarget指向事件所绑定的元素,而event.target始终指向事件发生时的元素。
- 分为errorCaptured与errorHandler。
- errorCaptured是组件内部钩子,可捕捉本组件与子孙组件抛出的错误,接收error、vm、info三个参数,return false后可以阻止错误继续向上抛出。
- errorHandler为全局钩子,使用Vue.config.errorHandler配置,接收参数与errorCaptured一致,2.6后可捕捉v-on与promise链的错误,可用于统一错误处理与错误兜底。
- Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。
-
- 举例来说,ES6在Array对象上新增了Array.from方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。
-
- Babel默认不转码的API非常多,详细清单可以查看babel-plugin-transform-runtime模块的definitions.js文件。
也可以在当前项目部署的端口主目录下存放favicon.ico文件,默认就会显示该图标
因为箭头函数默绑定父级作用域的上下文,所以不会绑定vue实例,所以 this 是undefind
- this.$parent拿到父组件实例
- this.$children拿到子组件实例(数组)
访问根实例、访问父组件、子组件
用尤大的话说就是各种速度提升n倍。我希望在依赖node_modules能够做出调整,文件数目太多,开启项目每次都要下载
- 在标签上绑定了自定义属性,防止css全局污染
- 但是很多时候使用ui框架如果加scope就不能覆盖,这个时候一般写sass 会在最外层包裹该组件名的id 就可以不使用scoped 了
- 让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。
-
- 返回的对象可以直接用于渲染函数和计算属性内,并且会在发生改变时触发相应的更新;也可以作为最小化的跨组件状态存储器。
- <template comments>
- ...
- </template>
- 初始状态下设置data数据的默认值,重置时直接Object.assign(this.$data, this.$options.data())
-
- 说明:
- this.$data获取当前状态下的data
- this.$options.data()获取该组件初始状态下的data(即初始默认值)
- 如果只想修改data的某个属性值,可以this[属性名] = this.$options.data()[属性名],如this.message = this.$options.data().message
- key的作用主要是为了高效的更新虚拟DOM;
- 如果没有唯一的key, 数据更新时, 相同节点更新前后无法准确一一对应起来,会导致更新效率降低;
- 当页面的数据发生变化时,Diff算法只会比较同一层级的节点:
-
- 如果节点类型不同,直接干掉前面的节点,再创建并插入新的节点,不会再比较这个节点以后的子节点;
- 如果节点类型相同,则会重新设置该节点的属性,从而实现节点的更新;
- 像1.0与2.0,我只知道一点-。-
- 1、 2.0生命生命周期变化感觉变得更加语义化一点(有规律可寻,更好记了),而且增加了beforeUpdate、updated、activated、deactivated,删除了attached、detached。
- 2、2.0将1.0所有自带的过滤器都删除了,也就是说,在2.0中,要使用过滤器,则需要我们自己编写,以下是一个自定义过滤器示例,
- Vue.filter('toDou',function(n,a,b){
- return n<10?n+a+b:''+n;
- });
-
- 优先级A的规则:必要的 (规避错误)
-
- 组件名为多个单词
- 组件数据:组件的 data 必须是一个函数。
- 细致的 Prop 定义
- 总是用 :key 配合 v-for
- 避免 v-if 和 v-for 用在一起
- 为组件样式设置作用域
- 私有属性名:自定义私有属性使用 $_ 前缀。并附带一个命名空间以回避和其它作者的冲突 (比如 $_yourPluginName_)。
-
-
- 优先级B的规则:强烈推荐 (增强可读性)
-
- 组件文件:只要有能够拼接文件的构建系统,就把每个组件单独分成文件。
- 单文件组件文件的大小写:要么始终是单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)。
- 基础组件名:应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。
- 单例组件名:只拥有单个活跃实例(每个页面只使用一次)的组件应该以 The 前缀命名,以示其唯一性。
- 紧密耦合的组件名:和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
- 组件名中的单词顺序:组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。
- 自闭合组件:在单文件组件、字符串模板和 JSX 中没有内容的组件应该是自闭合的——但在 DOM 模板里永远不要这样做。
- 模版中的组件名大小写:在单文件组件和字符串模板中组件名应该总是 PascalCase 的——但是在 DOM 模板中总是 kebab-case 的。
- JS / JSX 中的组件名大小写:JS/JSX 中的组件名应该始终是 PascalCase 的,在较为简单的应用中只使用 Vue.component 进行全局组件注册时,可以使用 kebab-case 字符串。
- 完整单词的组件名
- Prop 名大小写:在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 kebab-case。
- 多个特性的元素:多个特性的元素应该分多行撰写,每个特性一行。
- 模板中简单的表达式:组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。
- 简单的计算属性
- 带引号的特性值:非空 HTML 特性值应该始终带引号 (单引号或双引号,选你 JS 里不用的那个)。
- 指令缩写:指令缩写 (用 : 表示 v-bind: 、用 @ 表示 v-on: 和用 # 表示 v-slot:) 应该要么都用要么都不用。
-
-
- 优先级C的规则:推荐 (将选择和认知成本最小化)
-
-
- 组件 / 实例的选项的顺序
- 元素特性的顺序
- 组件 / 实例选项中的空行:在多个属性之间增加一个空行,特别是在这些选项一屏放不下,需要滚动才能都看到的时候。
- 单文件组件的顶级元素的顺序:总是让 <script>、<template> 和 <style> 标签的顺序保持一致。且 <style> 要放在最后,因为另外两个标签至少要有一个。
-
-
- 优先级D的规则:谨慎使用 (有潜在危险的模式)
-
- 没有在 v-if / v-else-if / v-else 中使用 key
- 元素选择器应该避免在 scoped 中出现。
- 隐性的父子组件通信:应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop。
- 非 Flux 的全局状态管理:应该优先通过 Vuex 管理全局状态,而不是通过 this.$root 或一个全局事件总线。
-
- 结构: 输入部分( input )和输出部分( ul )
- 逻辑:用户输入之后,通过事件触发拿到用户输入的数据存起来,
- 将用户数据集合通过 v-for 渲染到页面上
- 当用户点击清单项,通过事件触发移出对应事件
双向绑定的原理是基于Object,defineProperty的,ie8及以下不兼容这个api。另外还有一些特性至少在ie10才能用,我们已经弃用ie了,所以没继续总结
让我选肯定是vue。angularjs没用过。angular倒是用过。挺好用的,但是!!!编译真的很慢,加了热更新还是慢,开发毫无用户体验,按ctrl+s等2秒的绝望,你没用过angular不会明白的。react没在项目中用过,自己玩过几个小项目,使用体验一般般,jsx写起来真的很别扭
- activated和deactivated
-
- keep-alive的生命周期
- 1.activated: 页面第一次进入的时候,钩子触发的顺序是created->mounted->activated
- 2.deactivated: 页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated
- 1.SSR服务器渲染;
- 2.静态化;
- 3.预渲染prerender-spa-plugin;
- 4.使用Phantomjs针对爬虫做处理
不是应该 require('@/assets/images/xxx.png') 这样吗??你这样多浪费资源啊 @chenqim
- 上面蛮多回答感觉像开玩笑似的,静态资源图片失效分几种情况。
- 1、确定线上环境是否在根路径上,配置资源根目录,vue-cli2 和 vue-cli3 字段不一致(assetsPublicPath 和 publicPath ),如果项目是根路径上,用'/','./'都行,如果是在'/hc'这个路径上,用'./' 相对路径(需history模式),也可以用'/hc/'。 在'/hc'路径上,如果需要本地和线上保持一致,可以用环境做判断设置不同的publicPath值。
- 2、确定静态文件放置的位置。
- ①、如果放在public/static,不经过webpack打包, 放在public 又分使用绝对路径和相对路径。
- ②、如果放在assets, 经过webpack打包, 使用的是相对路径
- 3、路径是否是动态的,如果是动态,需要用require() 引入。
-
- 第一种:挂载到Vue的prototype上。把全局方法写到一个文件里面,然后for循环挂载到Vue的prototype上,缺点是调用这个方法的时候没有提示
-
- Object.keys(tools).forEach(key => {
- Vue.prototype[key] = tools[key]
- })
-
- 第二种:利用全局混入mixin,因为mixin里面的methods会和创建的每个单文件组件合并。这样做的优点是调用这个方法的时候有提示
-
- Vue.mixin(mixin)
- new Vue({
- store,
- router,
- render: h => h(App),
- }).$mount('#app')
-
- import tools from "./tools"
- import filters from "./filters"
- import Config from '../config'
- import CONSTANT from './const_var'
-
- export default {
- data() {
- return {
- CONFIG: Config,
- CONSTANT: CONSTANT
- }
- },
- methods: {
- // //将tools里面的方法挂载到vue上,以方便调用,直接this.$xxx方法名就可以了
- // Object.keys(tools).forEach(key => {
- // Vue.prototype[key] = tools[key]
- // })
- //将tools里面的方法用对象展开符混入到mixin上,以方便调用,直接this.$xxx方法名就可以了
- ...tools
- },
- filters: {
- // //将filter里面的方法添加了vue的筛选器上
- // Object.keys(filters).forEach(key => {
- // Vue.filter(key, filters[key])
- // })
- ...filters
- }
- }
-
-
- 在method中定义方法
-
- htmlFilter(htmlString){
- return htmlString.replace(/+s/g,’’)
- }
-
- 在vue中 v-html="htmlFilter(htmlString)"即可
- 源码 中的 initData() 方法
- if (methods && hasOwn(methods, key)) { warn( Method “${key}” has already been defined as a data property., vm ) }
- 会取出 methods 中的方法进行判断,也就是 hasOwn(methods, key)
- 如果此 key 值 在 methods 中存在,会有warn 警告哦****
start
1 项目类型
前端的项目目前来看主要分为小程序开发,H5页面开发、PC官网、后台管理系统开发、Native开发。不同的项目所涉及的知识点和环境不太一样,但是很多方面是相通的。
1.1小程序
由于框架限定在Vue,所以这里指的是使用mpvue、WePY来开发小程序项目。
1.2H5页面
这里主要是指微信页面、Webview中的H5页面开发
1.3 PC官网
为什么单独划出来是因为官方的开发主要是用来展示企业信息、产品,对交互、体验有一定的要求,会有一些炫酷的动画效果。还有就是官网有可能需要采用SSR(比如Vue的Nuxt.js)来做,来确定良好的SEO。
1.4后台管理系统
后台管理系统主要功能在于数据的配置、权限的控制、数据报表的展示、日志功能等。通常又叫CMS,OA。
1.5 Native开发
这个通常就是指用前端技术去开PC应用、APP应用,比如Weex, Electron。
1.6 通吃型
比如uni-app, 可以一套代码编译成不同的平台源码。
不同的项目类型决定了其能够使用的生态、目录结构、特定的上下文。这里就以后台管理系统为例来说一下如何基于Vue来搭建一个项目。
注: 我只会玩这个,凑合阅读吧
基于@vue/cli的选型
后台管理系统中vue-router,vuex都是必选的,其它可以自行考虑。
ES6/7 or Typescript ?
鉴于目前Typescript如此流行,很多流行的框架和库都采用其来写,IDE友好的智能提示、强类型结束等,在立项时是否考虑采用Typescript来写Vue项目。如果采用Typescript,是不是很羡慕Angular中的DI注入,那可以考虑在大型项目中引入inversify这个库;在开发过程中遇到一些库没有声明文件要学会定义声明文件,这个是Typescript初学者最头疼的问题。
- 还有一个问题是团队中有多少人会Typescript,项目周期紧不紧,有没有时间来试错,踩坑。
-
- Sass/Less/Stylus/PostCss ?
- 由于Vue项目开发本身样式自带scope,所以不需要像React那样去选css-in-js框架(目前在React最流行的是styled-components),但是如果我们在Vue中采用JSX的方式来定义组件,是否考虑引入vue-styled-components这个库(年久失修,完全脱节React版了,但依然是Vue中最好的选择)。在Vue中sass, less, stylus可以在<style>标签中通过lang=""来指定,如果你想使用PostCss也可以的,就是要自己花点时间去折腾一下。
- 关于代码规范和风格
- 这个主要的选择就是Prettier 和 Airbnb风格,如果配置不好,在IDE中满屏的红色波浪线和黄色的小灯光提示。
-
- 在配置eslint或者tslint时主要考虑的点是是否要写分号,未定义变量等问题。
-
- 关于测试
-
- 很多时间前端项目测试反而拖慢了项目的开发进度,但是在开源项目中良好的测试是保证项目质量的一个很重要方式。这里通常分为单元测试(Unit Testing)和端到端测试(E2E Testing),更多信息我也没有什么经验,自行百度、Google。
通过 @vue/cli 生成项目后,接下来就是添加一些配置文件
通用配置
一个前端项目在开发过程中少不了各种框架、IDE的配置文件。前端项目的配置文件通常格式有xx.json、.xxrc、xx.config.js、xxconfig等方式。
2编辑器配置:.editorconfig
这里最重要的是缩进方式,及Tab大小,建议2个空格作用缩进。
- https://editorconfig.org
- root = true
-
- [*]
- charset = utf-8
- indent_style = space
- indent_size = 2
- end_of_line = lf
- insert_final_newline = true
- trim_trailing_whitespace = true
-
- [*.md]
- insert_final_newline = false
- trim_trailing_whitespace = false
Git忽略文件配置: .gitignore
这里的配置决定了哪些文件会被版本控制所忽略
See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2.1dependencies
/node_modules
/.pnp
.pnp.js2.2testing
/coverage2.3production
/build2.4 misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.localnpm-debug.log*
yarn-debug.log*
yarn-error.log*3 editor
/.idea
Eslint配置: .eslintrc.js,.eslintignore等
说实话eslint要是配置不好,代码在IDE中提示真的很恶心,但是配置项又太多,还有很多专有的扩展,这里给出我的一个配置(也是到处copy过来的)
- module.exports = {
- root: true,
- env: {
- node: true
- },
- extends: [“plugin:vue/strongly-recommended”],
- rules: {
- “no-console”: process.env.NODE_ENV === “production” ? “error” : “off”,
- “no-debugger”: process.env.NODE_ENV === “production” ? “error” : “off”,
- // 不加分号
- “semi”: [0],
- // 不能有未定义的变量
- “no-undef”: 1,
- // 不能有声明后未被使用的变量或参数
- “no-unused-vars”:[2, {
- “vars”: “local”,
- “args”: “none”
- }],
- // 禁止修改const声明的变量
- “no-const-assign”: 2,
- // 函数参数不能重复
- “no-dupe-args”: 2,
- // 如果if语句里面有return,后面不能跟else语句
- “no-else-return”: 2,
- // 块语句中的内容不能为空
- “no-empty”: 2,
- // 禁止对null使用==或!=运算符
- “no-eq-null”: 2,
- // 禁止扩展native对象
- “no-extend-native”: 2,
- // 禁止不必要的函数绑定
- “no-extra-bind”: 2,
- // 禁止非必要的括号
- “no-extra-parens”: 2,
- // 禁止多余的冒号
- “no-extra-semi”:2,
- // 禁止省略浮点数中的0 .5 3.
- “no-floating-decimal”: 2,
- // 禁止行内备注
- “no-inline-comments”: 0,
- // 不能有不规则的空格
- “no-irregular-whitespace”: 2,
- // 不能用多余的空格
- “no-multi-spaces”: 1,
- // 禁止重复声明变量
- “no-redeclare”: 2,
- // 禁止使用javascript:void(0)
- “no-script-url”: 0,
- // 禁止稀疏数组, [0,2]
- “no-sparse-arrays”: 2,
- // 禁止使用三目运算符
- “no-ternary”: 0,
- // 一行结束后面不要有空格
- “no-trailing-spaces”: 1,
- // 标识符不能以_开头或结尾
- “no-underscore-dangle”: 1,
- // 是否允许非空数组里面有多余的空格
- “array-bracket-spacing”: [2, “never”],
- // 箭头函数用小括号括起来
- “arrow-parens”: 0,
- // =>的前/后括号
- “arrow-spacing”: 0,
- // 块语句中使用var
- “block-scoped-var”: 0,
- // 逗号风格,换行时在行首还是行尾
- “comma-style”: [2, “last”],
- // 避免不必要的方括号
- “dot-notation”: [0, { “allowKeywords”: true }],
- // 必须使用全等
- “eqeqeq”: 2,
- // 对象字面量中冒号的前后空格
- “key-spacing”: [0, {
- “beforeColon”: false,
- “afterColon”: true
- }],
- // 变量声明后是否需要空一行
- “newline-after-var”: 0,
- // 引号类型 `` “” ‘’
- “quotes”: [1, “single”],
- // 变量声明时排序
- “sort-vars”: 0,
- // 禁止比较时使用NaN,只能用isNaN()
- “use-isnan”: 2,
- //jsx中使用单引号
- “jsx-quotes”: [“error”, “prefer-single”],
- // 单个组件无内容自结尾
- “vue/html-self-closing”: [“error”, {
- “html”: {
- “void”: “always”,
- “normal”: “always”,
- “component”: “always”
- },
- “svg”: “always”,
- “math”: “always”
- }],
- // 设置html缩进
- “vue/html-indent”: [“error”, 2, {
- “attribute”: 2,
- “baseIndent”: 1,
- “closeBracket”: 0,
- “alignAttributesVertically”: false,
- “ignores”: []
- }],
- // 属性顺序
- “vue/attributes-order”: 1,
- // 注释前面需要添加空格
- “spaced-comment”: [“error”, “always”, { “exceptions”: ["-", “+”] }],
- // html属性赋值等号左右不能有空格
- “vue/no-spaces-around-equal-signs-in-attribute”: [“error”],
- // 强制prop以驼峰命名
- “vue/prop-name-casing”: [“error”, “camelCase”],
- // 移除多余不使用的空格
- “vue/no-multi-spaces”: [“error”, {
- “ignoreProperties”: false
- }],
- // html结尾 >
- “vue/html-closing-bracket-newline”: [“error”, {
- “singleline”: “never”,
- “multiline”: “never”
- }],
- // 属性每行数量
- “vue/max-attributes-per-line”: [“error”, {
- // 一行最多3个属性
- “singleline”: 3,
- “multiline”: {
- “max”: 1,
- “allowFirstLine”: true
- }
- }],
- // 单行html元素内容是否换行
- “vue/singleline-html-element-content-newline”: [“error”, {
- “ignoreWhenNoAttributes”: true,
- “ignoreWhenEmpty”: true,
- “ignores”: [
- “pre”,
- “textarea”,
- “span”,
- “i”,
- “label”,
- “el-button”,
- “el-radio”,
- “el-checkbox”,
- “el-link”,
- “el-tab-pane”,
- “el-dropdown-item”,
- “el-step”,
- “el-table-column”,
- “el-option”
- ]
- }]
- },
- parserOptions: {
- parser: “babel-eslint”
- }
- };
PostCss配置: postcss.config.js
这个文件自动生成,里面的内容就是指定autoprefixer兼容配置
Babel配置: babel.config.js
主要是配置Babel的plugins、presets和parse等
StyleLint:.stylelintrc
如果代码对样式有一定的规范的话,可以加一个,没有就不需要配置这个。
- {
- “extends”: “stylelint-config-standard”,
- “plugins”: [“stylelint-scss”]
- }
@vue/cli配置:vue.config.js
在这个里面我们可以对@vue/cli的Webpack进行配置和覆盖。
- module.exports = {
- devServer: {
- proxy: {
- ‘/kpi’: {
- target: process.env.VUE_APP_KPI_API,
- changeOrigin: true
- }
- }
- }
- }
Webpack配置:webpack.config.js
因为在webpack中不能识别@vue/cli中的@路径,所以需要一个配置文件让webapck提示正常。具体怎么配置可以自行搜索。
- ‘use strict’
- const path = require(‘path’)
-
- function resolve (dir) {
- return path.join(__dirname, ‘.’, dir)
- }
-
- module.exports = {
- context: path.resolve(__dirname, ‘./’),
- resolve: {
- extensions: [’.js’, ‘.vue’, ‘.json’],
- alias: {
- ‘@’: resolve(‘src’),
- ‘_c’: resolve(‘src/components’)
- }
- }
- }
Visual Studio Code配置:.vscode目录
这里主要是配置基于vscode的代码调试以及eslint配置。
版本控制
不管是多人协作开发还个一个人开发在使用git时都需要一套流程规范来执行。
Git Flow
这个每个团队的做法不太一样,有的采用多分支开发,有的采用单一master分支开发,有的还采用submodule的方式,有的在项目中使用了lerna来做多packages,甚至有的公司一个分支一个项目。
在开发环境的区分上通常分为生产(线上)环境、预发布环境、开发环境,有的还有什么沙盒环境,很多做得好的公司基于Docker前后端都可以根据每一个commit来发布。
有时候不想把有些代码提交上去,除了选择性提交单个文件外,还有使用git的stash功能,此外如果使用Webstorm可还可以使用其提供的Changelist来缓存修改,切换分支。
Git Commit
项目提交的描述如果没有一定的规范,随性而为的话,就会让其它人误解。通常提交采用英文作为描述,可以多行文字。在社区中有很多流行的方案(比如Conventional Commit),更多的是采用Angular的方式。
Change Log
如果采用了社区统一的commit方式,那么我们就可以基于提交来生成变更记录,在每一次版本发布时自动关联Jira中的Issue。
版本号生成
这个通常是按照Semantic Versioning的规范来打tag,具休怎么做可以自行尝试
在项目中通常使用gitHooks和husky这二个node包来配置上面提到的这些。在git钩子中我们在每次提交、push前跑一次单元测试、代码覆盖率。前端代码覆盖率一般来说没有必要加,不然很痛苦。
下面是package.json文件中相关的配置示例(试验性代码)
- {
- “name”: “your-project-name”,
- “version”: “0.1.0”,
- “scripts”: {
- “clean”: “rm -rf node_modules”,
- “serve”: “vue-cli-service serve”,
- “build”: “vue-cli-service build”,
- “lint”: “vue-cli-service lint --no-fix”,
- “stylelint”: “stylelint src/**/.{scss,css,less,css,vue,jsx} --fix",
- “eslint”: “eslint --ext .js,.jsx,.vue src --fix”,
- “changelog”: “conventional-changelog -p angular -i CHANGELOG.md -s -r 0”
- },
- “repository”: {
- “type”: “git”,
- “url”: “http://gitlab.transsion.com/mi/mi-bigdata-admin.git”
- },
- “dependencies”: {},
- “devDependencies”: {
- “@commitlint/cli”: “^8.1.0”,
- “@commitlint/config-conventional”: “^8.1.0”,
- “babel-eslint”: “^10.0.1”,
- “conventional-changelog-cli”: “^2.0.23”,
- “eslint”: “^6.2.1”,
- “eslint-plugin-vue”: “^5.2.3”,
- “husky”: “^3.0.4”,
- “lint-staged”: “^9.2.3”,
- “stylelint”: “^10.1.0”,
- “stylelint-config-standard”: “^18.3.0”,
- “stylelint-scss”: “^3.9.4”,
- },
- “gitHooks”: {
- “pre-commit”: “lint-staged”
- },
- “lint-staged”: {
- ".{js,vue}”: [
- “vue-cli-service lint”,
- “eslint --fix --ext .js,.vue src”,
- “git add”
- ],
- “*.{css,scss,less,vue}”: [
- “stylelint --fix”,
- “git add”
- ]
- },
- “husky”: {
- “hooks”: {
- “commit-msg”: “commitlint -E HUSKY_GIT_PARAMS”
- }
- }
- }
项目文档和组件测试文档
除了在项目根目录放一个README.MD文件外,通常还需要一些比如CHANGELOG.md, PLAD.md等文档,还有一些组件的使用文档,可以考虑使用styleguide和storybook。
持续集成和部署
目前开源项目通常采用Travis,而一般公司内部项目通常采用Jenkins来做持续集成,在部署上通常采用Docker,集群上使用KubeOperator来管理。
4 API请求方式
通常采用Restfull的方式来请求数据,也可以采用GraphQL的方式来请求。如果采用Restfull的方式通常可以使用axios, fetch api。GraphQL可以使用Apollo Client。
代理和数据Mock
SPA页面开发通常都是配置代码来调用后端的接口数据,怎么配置可以参考@vue/cli文档。数据Mock主要用到一个mockjs,至于怎么起服务自行搜索。
项目用到的库
下面这些库可以在所有项目中使用
- UI框架: Element, iView, vue-strap等
-
- 注:UI风格目前有Bookstrap、Antd和Google Materials三种风格,在项目搭建时这也是一个很重要的技术选型。
-
- 日期: moment, dayjs
-
- URL解析: query-string, path-to-reqexp
-
- 实用方法: lodash
-
- Cookie: js-cookie
-
- 混淆ID: hashids
-
- 图表: echarts
-
- Ajax: axios, isomorphic-fetch, vue-apollo
-
- 拖拽: Vue.Draggable
-
- Meta修改: vue-meta
-
- 注:这些只是我能想到的
5项目目录划分
- 视图页面放在 pags或者views中
- 静态文件放在static中
- 资源文件放在assets中
- 样式文件放在styles中
- 辅助库放在utils中
- 配置文件可以放在config或者constants中
- vuex的文件放在stores中,至于getters, actions, mutation, modules可以参考vuex的文档
- 路由文件放在routes中
- 所有组件放在components中
- 共享代码也可以使用shared作为目录
- 布局组件可以放在layouts目录中
权限配置
主要分为页面权限(路由)、功能权限,采用多级角色划分方式。菜单配置数据直接通过接口返回
接著建好專案後,通常會依照需求裝入以下插件:
- svg-loader - 將 svg 作為組件使用
- axios
- dayjs - 以往常用的 moment.js 除了既有舊專案外,構建團隊今年中建議改採其他更為輕量的 library
- bootstrap-vue 看設計稿,如果是需要手刻的就偏向引入頁面結構組件如 b-row
專案目錄大致如下,將剛剛預先規劃的 component、views 先建立好,接著便可以開始切分組件 css:
- src
- ├── App.vue
- ├── assets
- │ ├── img
- │ │ ├── access_time-24px.svg
- │ │ ├── accessibility_new-24px.svg
- │ │ ├── add_circle_outline-24px.svg
- │ │ ├── alarm.svg
- │ │ ├── apps-24px.svg
- │ └── scss
- │ ├── abstracts
- │ ├── base
- │ ├── main.scss
- │ └── plugin
- ├── components
- │ ├── Base
- │ │ ├── BaseCard.vue
- │ │ ├── BaseCol.vue
- │ │ ├── BaseLoadCard.vue
- │ │ ├── BaseRow.vue
- │ │ └── FlexSystem.md
- │ ├── Home
- │ │ ├── HomeChart.js
- │ │ ├── HomeItem.vue
- │ │ ├── HomeNavbar.vue
- │ │ ├── HomeSideBar.vue
- │ │ └── HomeSortbar.vue
- │ └── Information
- │ └── InformationChart.js
- ├── main.js
- ├── router
- │ └── index.js
- ├── service
- │ ├── api.js
- │ └── dayFormate.js
- ├── store
- │ └── index.js
- └── views
- ├── Home.vue
- └── Information.vue
頁面路由及組件樣式切分完成,接著便可以開始開發功能以及串接資料。
END
负责组装数据,以另外一种形式或外观展现数据。
优点:
- 可维护性(后期改起来方便);
- 可扩展性(想要增加功能,增加需求方便);
- 开发效率提高(程序逻辑组织更好,调试方便);
- 看起来舒服(不容易写错)
解答
git地址: https://github.com/janl/mustache.js
模板引擎技术是非常有用的,所以它不是一个冷门的知识,反而是需要我们去了解的;
所以我们通过github的安装指令,一步一步的来实现基本的api
我搜查了很多资料,网上并没有一个使用npm包的方式来做demo,都是使用render来执行渲染,鉴于我们日常工作中使用npm比较多,我做一版npm的demo
首先npm init初始化一个空项目;
然后安装mustache
npm i mustache -s -d
在package.json中填写如下内容:
- "scripts": {
-
- "build": "mustache dataView.json myTemplate.mustache>public/output.html",
-
- },
-
如同这个指示看到的一样,我们需要创建一个json文件,这个文件就是变量配置文件,还需要创建一个模板文件,这个模板文件相当于执行render函数的文件,这种方式更加一目了然;
我们创建它们,我使用了vscode,并且装了相关的mustache的插件,所以语法会有提示;
我们首先在json文件中写入一个对象,里面写一个值,然后在模板文件中使用{{}}来执行渲染;
然后执行
npm run build
就会发现在public下面生成了一个html文件,如果报错,说明你的文件目录跟我的不一样
我们在json文件中,写入了这么多值,有普通的值,有布尔值,有数组等等
- "age": "19",
-
- "html": "<p>123</p>",
-
- "isTrue": true,
-
- "thisIsObject": {
-
- "name": "shenhao",
-
- "age": "19"
-
- },
-
- "isArray": [{
-
- "name" : "shenhao"
-
- },{
-
- "name" : "shenhao"
-
- },{
-
- "name" : "shenhao"
-
- }]
-
- }
我们在模板中写出了这些代码,我在模板中写了一个简单的html
- <!DOCTYPE html>
-
- <html lang="en">
-
- <head>
-
- <meta charset="UTF-8">
-
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
-
- <title>Document</title>
-
- </head>
-
- <body>
-
- {{name}} 有一个 {{&html}}
-
- <br>
-
- <hr>
-
- {{thisIsObject.name}} {{thisIsObject.age}}
-
- <br>
-
- <hr>
-
- -webkit- {{#isTrue}} 如果是真就显示了 {{/isTrue}}
-
- <br>
-
- <hr>
-
- 循环一下下面的内容, 如果是数组,可以用.来表示循环的每一个元素
-
- {{#isArray}} {{name}} {{/isArray}}
-
- <br>
-
- <hr>
-
- {{!^}}与{{!#}}相反,如果变量是null、undefined、 false、和空数组讲输出结果
-
-
-
- </body>
-
- </html>
我们来讲解一下基本的api
{{name}}:会在json中查询对应的值,并且渲染
{{&html}}: html在json中如果式一个html标签,可以用这样的方式进行转义 (类似vue中的v-html)
{{#boolean}} 和 {{/boolean}}: 是一个组合,如果boolean为真那么它们之间的内容会渲染,否则不会
{{^boolean}}: 和上面用法一样,只不过是上面的else
{{object.name}}: 同样支持对象键值对的方式获取
{{#array}} 和 {{/array}}: 如果这样写是一个数组,那么不仅有判断boolean的真假,它会迭代中间可以写迭代中的每一个元素,每一个元素可以用{{.}}来获取,如果要获取迭代中的内容是一个键值对,那么可以直接使用{{name}}
这就是mustache简单的用法,上面有demo,你们可以对着demo敲一遍就能非常easy的理解了;
END
- v-model 為一個語法糖,Vue 會默認使用一個名為 value 的 prop,以及名為 input 的事件。
-
- 為了避免不同的 value 有不同的作用,現在可以使用下面的方式自訂義自己想要的 v-model 行為。
-
- model: {
- prop: ‘checked’,
- event: ‘change’
- }
-
- 如果想要更改 checked 這個 prop 可以在 Vue 的 instance 中用以下這行程式發送 change 這個 event,並將目標的變動值傳給 checked 這個 prop。
-
- this.$emit(‘change’, $event.target.value);
- 使用Vue-i18n 加载不同的语言配置文件,
- 重要是初始化时,判断当前语言环境,要想好存储方案
-
- 采用i18n来解决国际化问题,关于语言环境的存储方案,看到有同学解答localStorage的方式,这种还是采用Cookie的存储方法,通过路由实现不同模块加载不同的国际化配置文件
- 莫名其妙的问题。可以同名,但data会覆盖methods。并且本就不该同名,同名说明你命名不规范。
- 然后解释为什么会覆盖,因为Props、methods、data、computed、watch都是在initState函数中被初始化的。初始化顺序就是我上面给出的顺序,本质上这些都是要挂载到this上面的,你如果重名的话,后面出现的属性自然而然会覆盖之前挂载的属性了。如果你的eslint配置比较严格的话,同名是编译不通过的。
-
- 不可以,因为初始化vm的过程,会先把data绑定到vm,再把computed的值绑定到vm,会把data覆盖了
正确顺序:props、methods、data、computed、watch、
未完待续...
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。