vue.js 订阅
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。 [1] 展开全文
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。 [1]
信息
稳定版本
2.6.10
开发商
尤雨溪
软件授权
MIT License
软件名称
Vue.js
更新时间
2019年3月20日
初始版本
0.11
软件版本
vue0.11 vue0.12 vue1.0 vue2.0
软件语言
JavaScript
软件大小
生产版:76 KB,开发版:240 KB
Vue.js简介
Vue.js是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和Vue生态系统支持的库开发的复杂单页应用。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件 [2]  。Vue.js 自身不是一个全能框架——它只聚焦于视图层。因此它非常容易学习,非常容易与其它库或已有项目整合。另一方面,在与相关工具和支持库一起使用时 [3]  ,Vue.js 也能完美地驱动复杂的单页应用。
收起全文
精华内容
参与话题
问答
  • 目前最完整的前端框架 Vue.js 全面介绍

    万次阅读 多人点赞 2018-01-06 23:19:56
    Vue.js 是一个JavaScriptMVVM库,是一套构建用户界面的渐进式框架。 摘要 2016年最火的前端框架当属Vue.js了,很多使用过vue的程序员这样评价它,“vue.js兼具angular.js和react.js的优点,并...

    Vue.js 是一个JavaScriptMVVM库,是一套构建用户界面的渐进式框架。


    摘要


    2016年最火的前端框架当属Vue.js了,很多使用过vue的程序员这样评价它,“vue.js兼具angular.js和react.js的优点,并剔除了它们的缺点”。授予了这么高的评价的vue.js,也是开源世界华人的骄傲,因为它的作者是位中国人–尤雨溪(Evan You)。 


    Vue.js 是一个JavaScriptMVVM库,是一套构建用户界面的渐进式框架。它是以数据驱动和组件化的思想构建的,采用自底向上增量开发的设计。相比于Angular.js,Vue.js提供了更加简洁、更易于理解的API,使得我们能够快速地上手并使用Vue.js。


    调试插件


    在vue调试方面,可以选择安装chrome插件vue Devtools。打开vue项目,在console控制台选择vue面板。在Devtools工具中,可以选择组件,查看对应组件内的数据信息。也可以选择Vuex选项,查看该项目内Vuex的状态变量信息。 



    UI 组件库


    在vue组件库方面,个人不推荐使用UI组件库,毕竟自己造轮子的过程还是很有成就感的。当然,如果更重视开发效率,并且选择了vue2.0作为前端框架,那么饿了么推出的Element组件就是一个很不错的选择。其github项目(https://github.com/ElemeFE/element)更新比较频繁,虽然项目会有些不稳定,但是目前为止element就是最好的支持vue2.0的UI组件。就像它的口号一样,“快速成型,就为让你少加班”。 



    vue、React、Angular1 对比


    性能对比


    在Angular1中,在scope作用域中每一次数据变化,会触发watcher的重新计算,angular对常用的dom事件,xhr事件等做了封装, 在里面触发进入angular的digest流程。在digest流程里面,会从rootscope开始遍历, 检查所有的watcher。并且,如果一些 watcher 触发另一个更新,脏检查循环(digest cycle)可能要运行多次。Vue则没有这个问题,因为它使用基于依赖追踪的观察系统并且异步队列更新,数据的变化都是独立处罚的,除非数据之间有明确的依赖关系。 


    vue官方宣称vue的渲染性能优于react。为了有理有据让人信服,vue开发团队建立了一个简单的对比性能的项目(
    https://github.com/chrisvfritz/vue-render-performance-comparisons),它负责渲染10000个列表项100次。Vue官方将每一个参照项目都分别运行 20 次并取最好的结果结果如下图: 


     


    由此可见,Vue的性能是远好于Angular1,并且稍微优于React的。


    社区拓展对比


    Angular1的背后是Google,所以社区基础是不需要担心的,从Tutorial到StackOverflow的问题数量都可以反映出生态系统很完整。Angular1之后的2.0版本几乎是一个推翻重做的框架,对于使用了1.X版本的项目,想要平滑的升级过渡到2.0版本应该是非常困难的。


    现在Angular2的线上应用数量还不算太多,主流编码还是以1.X版本居多。这个版本化巨大的差异也间接影响到了开发者对于angular的信心。 


    Vue和React都有强大的社区支持。React有状态管理库Flux、ReduxVue,相应的,Vue有vuex。


    Vue 和 React 都提供了强大的路由库来应对大型应用。然而Vue的路由库和状态管理库都是由官方维护支持的。


    React 则是选择把这些问题交给社区维护,因此创建了一个更分散的生态系统。但相对的,React 的生态系统相比 Vue 更加繁荣。


    此外,Vue 提供了Vue-cli 脚手架,包括了Webpack,Browserify,甚至路由库,能让你非常容易地构建项目。


    学习陡峭度对比


    在指令与组件方面,Vue中将指令和组件分得更清晰。指令只封装 DOM 操作,而组件代表一个自给自足的独立单元,有自己的视图和数据逻辑。在 Angular1 中两者有不少相混的地方。在API与框架设计方面,angular1都比vue要复杂的多。就个人感觉而言,angular1和React的学习曲线会相对陡峭一些,而vue的编码方式会更趋近于前端开发者的编程习惯。 


    因为vue的作者是中国人,vue的官方网站、教程和api肯定是最完善、最易懂的。此外,每次大版本的发布,都会伴随着详尽的迁移说明文档,包含了很多详尽的阐述以及许多迁移的例子,甚至还有迁移工具。Angular的开发团队你们就不觉得脸红么… 
    Vue的使用非常的简单,创建一个本地的 .html 文件,然后通过如下方式引入 Vue:




    这样就生成了vue的hello world应用。


    渲染能力对比


    ReactNative能使你用相同的组件模型编写有本地渲染能力的 APP(iOS 和 Android)。能同时跨多平台开发,对开发者是非常棒的。为了弥补这方面的不足,在2016年9月举办的JSConf2016期间,vue.js的作者尤雨溪宣布加盟Weex团队担任技术顾问,双方将进行更紧密的合作,共建开发生态圈。


    Weex 是阿里的跨平台用户界面开发框架,Weex 的 JavaScript 框架运行时用的就是 Vue。在此之后,在 Weex 的帮助下,使用 Vue 语法开发的组件不仅仅可以运行在浏览器端,还能被用于开发 iOS 和 Android 上的原生应用。 


    Vue.js 的作者尤雨溪表示:“Weex选择Vue作为其JavaScript运行时框架是让我非常高兴的一件事。Vue的组件开发模式已经被web开发者社区广泛 认可,而把Vue的开发体验拓展到原生平台则是我一直想做但没有余力去做的事情。一想到Weex将能让开发者们用Vue的语法去写跨 Web/Android/iOS三端的通用组件,就让我很兴奋。”


    vue的缺点

        

    Vue就这么好,难道没有缺点吗?当然有,vue虽然在16年非常火爆,但是相比于angular和react,不论是成熟度还是社区活跃度都还不是对手。此外,Vue明确声明了自己放弃了对IE8的支持。再看看现在的招聘网站上,有多少写了需要有angular经验,而又有多少写了需要vue经验,就可见vue的影响力相比于angular和react还差的很远。


    vue全家桶及项目架构


    Vue有著名的全家桶系列,包含了vue-router(http://router.vuejs.org),vuex(http://vuex.vuejs.org), vue-resource(https://github.com/pagekit/vue-resource)。再加上构建工具vue-cli,就是一个完整的vue项目的核心构成。


    vue-router路由


    推荐使用npm工具来安装vue-router 


    npm install vue-router 


    通过import导入并定义Vue模块、vue-router模块和需要使用的组件,在本例中,分别是Goods、Ratings和Seller组件。最后,如果在一个模块化工程中使用它,必须要通过 Vue.use() 明确地安装路由功能。 


    import Vue from’vue’ 
    importRouter from’vue-router’

    import Goods from ‘@/components/goods/goods’; 
    import Ratings from ‘@/components/ratings/ratings’; 
    import Seller from ‘@/components/seller/seller’;

    Vue.use(Router); // 需要import Vue和Router,不然会报错undefined 


    通过const router= new VueRouter()来定义一个路由,并传入对应的配置,包括路径path和组件components。 




    最后,在使用newVue来创建和挂载vue根实例的时候,记得要通过 router配置参数注入路由,即在router中export出来的路由对象,从而让整个应用都有路由功能。 



    vuex状态管理


    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。如前面所提到的,Vuex 已经集成到 Vue 的官方调试工具vue Devtools,可以轻松的查看项目中的Vuex状态变化情况。

     
    假设有这样一个场景:我们的项目规模比较大,有多个父组件,每个父组件同时又包含多个子组件。如何保持对所有时间的追踪将变得很困难。到底哪个事件是哪个组件派发的,哪个组件该监听哪个事件?父组件将变得和子组件耦合越来越严重,因为它需要明确的派发和监听子组件的某些事件。项目逻辑分散在各个组件当中,很容易导致逻辑的混乱,不利于我们项目的维护。 


    这就是 Vuex 用来解决的问题。 Vuex 的四个核心概念分别是: 


    The state tree:Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个『唯一数据源(SSOT)』而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。 


    Getters:用来从 store 获取 Vue 组件数据。 


    Mutators:事件处理器用来驱动状态的变化。 


    Actions:可以给组件使用的函数,以此用来驱动事件处理器 mutations 


    Vuex和简单的全局对象是不同的,当Vuex从store中读取状态值的时候,若状态发生了变化,那么相应的组件也会高效的更新。并且,改变store中状态的唯一途径就是提交commit mutations。这样便于我们跟踪每一次状态的变化。只要发生了状态的变化,一定伴随着mutation的提交。 


    让我们来看一个最简单的vuex例子: 


     

    安装 Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutations: 


     

    现在,你可以通过 store.state 来获取状态对象,以及通过 store.commit 方法触发状态变更:


    vue-resource介绍


    Vue-resource有体积小,支持IE9以上的浏览器,支持promise特性的特点。同样推荐使用npm来安装Vue-resource。 


    $ npm install vue-resource 


    在安装并引入vue-resource后,可以基于全局的Vue对象使用http,也可以基于某个Vue实例使用http。

     
     


    在发送请求后,使用then方法来处理响应结果,then方法有两个参数,第一个参数是响应成功时的回调函数,第二个参数是响应失败时的回调函数。 


    vue-resource的请求API是按照REST风格设计的,它提供了7种请求API: 


    · get(url,[options]) 
    · head(url,[options]) 
    · delete(url,[options]) 
    · jsonp(url,[options]) 
    · post(url,[body], [options]) 
    · put(url, [body],[options]) 
    · patch(url,[body], [options])


    vue工程目录结构


    下图是一个简单的vue项目的大概结构,下面简要介绍一下每个文件夹中一般都会存放哪些内容。 


     


    components/文件夹用来存放Vue 组件。个人建议,把每一个组件中使用到的image图片放置到对应的组件子文件目录下,便于统一的管理 


    Node_modules/npm安装的该项目的依赖库 
    vuex/文件夹存放的是和 Vuex store 相关的东西(state对象,actions,mutations) 
    router/文件夹存放的是跟vue-router相关的路由配置项 
    build/文件是 webpack 的打包编译配置文件 
    static/文件夹存放一些静态的、较少变动的image或者css文件 
    config/文件夹存放的是一些配置项,比如服务器访问的端口配置等 
    dist/该文件夹一开始是不存在,在我们的项目经过 build 之后才会产出 
    App.vue根组件,所有的子组件都将在这里被引用 
    index.html整个项目的入口文件,将会引用我们的根组件 App.vue 
    main.js入口文件的 js 逻辑,在webpack 打包之后将被注入到 index.html 中


    vue中less的应用


     


    在vue项目中一样可以使用less预编译,只是需要使用npm安装less-loader插件。安装完成后,在vue中的css模块进行简单的配置,这样就可以直接使用less来编写样式表了。在打包编译的时候,会自动生成对应的css样式。


    vue合实例讲解Vue核心功能


    Vue的功能有很多,很难一一进行详细的解释。下面根据在工作中的项目实例,结合代码解释一下vue的几大核心功能。


    计算属性


    假设有如下的购物车结算场景,用户选中商品的总金额是根据商品数量、选中商品种类数 和商品单价来变化的。然而,数量、选中种类数量和单价这几个对象都是根据用户选择而动态变化的,如果在前端模版中为了计算最终商品总额,放入这几个动态变化的变量(商品数量、商品单价、选中商品种类),会让这个逻辑变得复杂难以维护。在这种情况下,模版便不再简洁清晰。Vue给出了此种场景的解决方案,在任何复杂的逻辑,vue都推荐使用计算属性。 


     


    如上图所示,在html中,我们只需要使用{{totalPrice}}这个计算属性就可以来表示最终的商品总额。我们不需要关注这个变量的数值变化,totalPrice这个变量的逻辑写在对应的computed计算属性中。 


    也许会有疑问,这个计算属性和定义一个method方法不是差不多么?这两者最大的区别是计算属性是基于它的依赖进行缓存的。计算属性只有在它的相关依赖发生变化时才会重新计算求值。在本例中,只有当选择商品的价格price和数量count发生变化时,这个计算属性totalPrice才会重新计算新的值。这就意味着,只要totalPrice这个值没有发生变化,多次访问该计算属性会立即返回之前的计算结果,而不必再次执行计算。


    模版语法


    Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。Vue的模版语法包括了使用双大括号插入文本、使用v-html插入纯HTML内容、使用v-bind插入对象、类似angular的v-if、v-show、v-for指令、以及过滤器等等。 



    组件化


    组件(Component)是 Vue.js 最强大的功能。组件可以封装可重用的代码,通过传入对象的不同,实现组件的复用。 


    举一个简单的组建例子,我们首先编写一个star组件,它就是一个普通的star.vue文件。它的作用就是简单实现了一个五角星。 


    如何在其他的vue文件中使用这个star组件呢?如下图所示,首先通过import引入star组件对象,并在想使用star组件的vue文件中声明注册star组件。现在就可以愉快的通过标签来在该vue文件中任意地方使用star组件了。在你想展示一个五角星的地方,使用一个star标签,就可以轻松完成这个功能。 


    组件实例的作用域是孤立的。这意味着不能在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,我们需要通过子组件的props选项。如本例所示,子组件star要显式的使用props选项声明它期待获得的数据。在这里就是指的“size”和“score”两个变量。我们可以通过父级给子组件star传入大小和数值这两个对象,来实现对子组件的定制化。 




    过渡效果


    Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果,可以用简单的几行代码实现酷炫的过渡效果。Vue 提供了 transition 的封装组件,在使用v-if、v-show等方法使得transition内部dom元素发生变化时,可以给任何元素和组件添加 entering/leaving 过渡。 


    当v-show中内容发生变化时,transition组件中的元素会发生状态的改变,在应用了transition封装后,Vue会自动识别目标元素是否应用了CSS过渡效果动画,如果有,会在合适的时机添加 entering/leaving的class来实现该过渡效果。 


    下图所示是一个简单的过渡效果的例子,需要将想实现过渡效果的元素放在transition标签中包裹,通过name=“slide-fade”来声明过渡效果名称,并在对应的vue文件中添加过渡效果 的css样式,这样就可以简单的完成该元素的过渡效果。 


     


    总结


    根据不完全统计,包括饿了么、苏宁易购、美团、天猫、Laravel、htmlBurger等国内外知名大公司都在使用vue进行新项目的开发和旧项目的前端重构工作。 


    此外,vue + vuex+ axios + vue-router + webpack + es6 + less的项目架构成为了越来越多大公司的第一选择。 

    展开全文
  • 1.学习使用vue.js(适合初学者)

    万次阅读 多人点赞 2018-01-02 10:17:03
    如果你不愿意多花时间去自学vue.js 官网的API,那么你可以在跟着下面的例子学习最实用,最容易上手的案例。首先要你必须要去VUE官网上下载vue.js,然后才能继续下面的步骤。 如果你不想下载的话,在有网络环境的...

    如果你不愿意多花时间去自学vue.js 官网的API,那么你可以在跟着下面的例子学习最实用,最容易上手的案例。

    首先要你必须要去VUE官网上下载vue.js,然后才能继续下面的步骤。
    如果你不想下载的话,在有网络环境的情况下,也可以选择绝对路径的vue.js。

    如果你想学习如何搭建webpack脚手架,那么请直接滑动到安装vue-cli

    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    

    当然,和vue的官网一样,不推荐新手直接安装vue-cli
    因为那样很容易把你绕晕。当然,如果你对node.js的构建工具比较熟悉的话,也可以直接安装vue-cli

    下面正式进入vue的学习:

    首先你引入了vue.js

    <script src="./vue.js"></script>
    

    然后你需要在html里面写一个大的盒子用来写你的整个vue的项目。

    <div id="app"></div>
    

    然后和vue建立关联

    <script>
    new Vue({
    	el : '#app',
    	data(){
    		return {
    			msg:'aaa'
    		}
    	},
    	methods:{//methods 存放fn的地方;
    		run(){
    			alert('run')
    		},
    		hover(){
    			alert('hover')
    		}
    	},
    	mounted(){//相当于js里的window.onload
    		this.run();//直接调用run函数;
    		console.log(this)
    	}
    });
    </script>
    

    下一篇:声明式渲染 和 指令绑定
    http://blog.csdn.net/heshuaicsdn/article/details/79196678


    安装使用vue-cli
    (不推荐新手使用!如果想要试用vue脚手架,那么还是先去JS里熟悉熟悉VUE的相关方法,再来使用vue-cli会轻松许多,如果你以前使用过其他框架(如wx小程序),对你使用vue-cli有很大帮助)

    首先,你需要下载安装node.js,如果你不确定安装没安装过node,执行node -v查看版本。如果没有显示
    这里写图片描述

    那么你就必须去https://nodejs.org/en/
    下载安装node.js 然后才能进行下面的操作;

    在网上有好多版本,有直接用npm安装的,也有用cnpm安装的,有用webpack-simple的,也有用webpack的,我先说说安装webpack-simple(就是简易版的webpack,他需要自己配置路由,某些方面不如webpack兼容好)

    安装vue-cli详细过程:
    搭建webpack脚手架
    1.新建一个目录;(为了方便,我就直接建在了桌面)
    这里写图片描述
    然后cd进入这个目录;
    这里写图片描述
    2.进入目录后像VUE官网说的:
    只需要执行:
    npm install vue
    安装最新版的vue

    3.然后还要安装脚手架:vue-cli(VUE的脚手架工具)
    npm install -g vue-cli
    安装好后可以执行vue list 来看看我们可以用哪些模版;
    这里写图片描述

    4.新建项目目录:
    如果你用webpack-simple模版可以执行:
    vue init webpack-simple hellovue

    如果你用webpack模版可以执行:(推荐使用,不过下载过程会比webpack-simple长)
    vue init webpack hellovue

    这里举得例子是“hellovue”,你可以写你自己的项目名称 
    

    这里写图片描述

    然后出现了一些选项:
    需要注意的是有一个问你是否使用 ESlint 语法,你选择no(因为这是一个更为严谨的语法,你如果代码风格不是特别严谨,最好还是选择no)
    然后其他都是yes,一路回车…
    这里写图片描述

    然后静静的等待下载完成…
    这里写图片描述

    下载好后根据提示执行命令:
    这里写图片描述

    5.cd hellovue
    如果没有依赖环境,执行
    npm install
    安装依赖;

    6.执行
    npm run dev

    这里写图片描述

    把红线部分,复制到浏览器的localhost 里;

    这里写图片描述

    要注意一下:package.json文件

    这里写图片描述

    这里写图片描述

    这里的"vue"和“vue-router”是我们下载的包。

    如:下载vue-router(路由)的时候:

    npm install vue-router -S
    

    当下载好以后,在你查看“package.json”里会多一个“vue-router”

    下一篇更新,路由
    请大家持续关注。。
    如果你发现哪里写的有错误,或者是你认为哪里可能有错误,请在下方评论,或者直接私信我,以便我能够更完善这篇文档,给大家提供更好的知识。

    展开全文
  • Vue.js 2.0之全家桶系列视频课程

    万人学习 2018-04-21 16:52:25
    基于新的Vue.js 2.3版本, 目前新全的Vue.js教学视频,让你少走弯路,直达技术前沿! 1. 包含Vue.js全家桶(vue.jsvue-router、axios、vuex、vue-cli、webpack、ElementUI等) 2. 采用笔记+代码案例的形式讲解,...
  • 从Hello World 开始,以案例的形式深入浅出的讲解Vue.js,最终实现本套课程的学习目标! 本套课程总共分五个章节 第一章讲解Vue的基础知识 通过第一章节可以掌握vue各种指令,比如v-for v-if 等等,掌握vue组件和...
  • vue.js有什么用,是用来做什么的(整理)

    千次阅读 多人点赞 2018-08-09 12:29:00
    vue.js有什么用,是用来做什么的(整理) 一、总结 一句话总结:用数据绑定的思想,vue可以简单写单个页面,也可以写一个大的前端系统,也可以做手机app的界面。   1、Vue.js是什么? 渐进式框架  自底向上...

    vue.js有什么用,是用来做什么的(整理)

    一、总结

    一句话总结:用数据绑定的思想,vue可以简单写单个页面,也可以写一个大的前端系统,也可以做手机app的界面。

     

    1、Vue.js是什么?

    渐进式框架 

    自底向上增量开发的设计

    易学习

    易整合

    Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。

     

    2、vue.js到底适合做哪种类型的界面?

    a、表单项繁多

    b、内容需要根据用户的操作进行修改

    Vue.js就是一个用于搭建类似于网页版知乎这种表单项繁多,且内容需要根据用户的操作进行修改的网页版应用。

     

    3、单页应用程序(SPA)是什么?

    一个页面就是应用(子应用)

    顾名思义,单页应用一般指的就是一个页面就是应用,当然也可以是一个子应用,比如说知乎的一个页面就可以视为一个子应用。单页应用程序中一般交互处理非常多,而且页面中的内容需要根据用户的操作动态变化。

     

    4、 前面说的网页版知乎我也可以用JQuery写啊,为什么要用Vue.js呢?

    a、产品是绝对需要反复修改的

    b、修改可能会导致DOM的关联与嵌套层次要发生改变从而使jquery结构相关代码变的异常复杂

    c、vue.js可以解决这个问题

    你是否还记得你当初写JQuery的时候,有写过('#xxx').parent().parent().parent()这种代码呢?当你第一次写的时候,你觉得页面元素不多,不就是找这个元素的爸爸的爸爸的爸爸吗,我大不了在注释里面写清楚这个元素的爸爸的爸爸的爸爸不就好了。但是万一过几天之后你的项目组长或者你的产品经理突然对你做的网页提出修改要求,这个修改要求将会影响页面的结构,也就是DOM的关联与嵌套层次要发生改变,那么(‘#xxx’).parent().parent().parent()可能就会变成$(‘#xxx’).parent().parent().parent().parent().parent()了。

    这还不算什么,等以后产品迭代越来越快,修改越来越多,而且页面中类似的关联和嵌套DOM元素不止一个,那么修改起来将非常费劲。而且JQuery选择器查找页面元素以及DOM操作本身也是有性能损失的,可能到时候打开这个页面,会变得越来越卡,而你却无从下手。

    这个时候如果你学过Vue.js,那么这些抱怨将不复存在。

     

    5、前端里面常说的视图层是什么?

    我们把HTML中的DOM就可以与其他的部分独立开来划分出一个层次,这个层次就叫做视图层。

    Vue 的核心库只关注视图层

     

    6、使用jquery开发完整页面的流程?

    a、html写构架

    b、css装饰

    c、js交互

    讲到JQuery,就不得不说到JavaScript的DOM操作了。如果你用JQuery来开发一个知乎,那么你就需要用JQuery中的各种DOM操作方法去操作HTML的DOM结构了。

    现在我们把一个网页应用抽象一下,那么HTML中的DOM其实就是视图,一个网页就是通过DOM的组合与嵌套,形成了最基本的视图结构,再通过CSS的修饰,在基本的视图结构上“化妆”让他们看起来更加美观。最后涉及到交互部分,就需要用到JavaScript来接受用户的交互请求,并且通过事件机制来响应用户的交互操作,并且在事件的处理函数中进行各种数据的修改,比如说修改某个DOM中的innerHTML或者innerText部分。

     

    7、Vue.js为什么能让基于网页的前端应用程序开发起来这么方便?

    a、有声明式

    b、响应式的数据绑定

    c、组件化的开发

    d、Virtual DOM

    因为Vue.js有声明式,响应式的数据绑定,与组件化的开发,并且还使用了Virtual DOM这个看名字就觉得高大上的技术。

     

    8、vue.js中常说的数据动态绑定是什么?

    就是vue.js会自动响应数据的变化情况,并且根据用户在代码中预先写好的绑定关系,对所有绑定在一起的数据和视图内容都进行修改。而这种绑定关系,在图上是以input 标签的v-model属性来声明的,因此你在别的地方可能也会看到有人粗略的称vue.js为声明式渲染的模版引擎。

     

    9、前端中为什么要组件化开发?

    a、非组件化开发代码和工作量都非常大

    b、修改起来生不如死

    但是现在我们做单页应用,页面交互和结构十分复杂,一个页面上就有许许多多的模块需要编写,而且往往一个模块的代码量和工作量就非常庞大,如果还按照原先的方法来开发,那么会累死人。而且遇到以后的产品需求变更,修改起来也非常麻烦,生怕动了其中一个div之后,其他div跟着雪崩,整个页面全部乱套,或者由于JavaScript的事件冒泡机制,导致修改一些内层的DOM事件处理函数之后,出现各种莫名其妙的诡异BUG。

     

    10、前端中如何进行组件化开发?

    a、借用的后端的面向对象中的模块化思想(把一些大功能拆分成许多函数,然后分配给不同的人来开发)

    b、把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑)

    在面向对象编程中,我们可以使用面向对象的思想将各种模块打包成类或者把一个大的业务模块拆分成更多更小的几个类。在面向过程编程中,我们也可以把一些大功能拆分成许多函数,然后分配给不同的人来开发。

    在前端应用,我们是否也可以像编程一样把模块封装呢?这就引入了组件化开发的思想。

    Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑),然后整个应用就算做完了。

     

    11、为什么有Virtual DOM技术?

    a、问题

    现在的网速越来越快了,很多人家里都是几十甚至上百M的光纤,手机也是4G起步了,按道理一个网页才几百K,而且浏览器本身还会缓存很多资源文件,那么几十M的光纤为什么打开一个之前已经打开过,已经有缓存的页面还是感觉很慢呢?

    b、原因

    (1)、浏览器本身处理DOM也是有性能瓶颈的

    (2)、用JQuery或者原生的JavaScript DOM操作函数对DOM进行频繁操作的时候,浏览器要不停的渲染新的DOM树

    这就是因为浏览器本身处理DOM也是有性能瓶颈的,尤其是在传统开发中,用JQuery或者原生的JavaScript DOM操作函数对DOM进行频繁操作的时候,浏览器要不停的渲染新的DOM树,导致页面看起来非常卡顿。

     

    12、Virtual DOM如何实现?

    a、预计算dom的各种操作,把最后一次的结果渲染出来(减少dom的渲染次数)

    而Virtual DOM则是虚拟DOM的英文,简单来说,他就是一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。最后在计算完毕才真正将DOM操作提交,将DOM操作变化反映到DOM树上。

     

    13、到底该怎么用Vue.js做单页应用开发?

    其实可以直接看学习视频开始干,应该是最好的

    a、介绍 - vue.js官方文档的基础部分硬着头皮看一遍

    我的建议是,先把介绍 - vue.js官方文档的基础部分硬着头皮看一遍。除了组件这个小节涉及到了很多晦涩难懂的名词以外,前面几章完全就是把Vue.js当作一个模版引擎来用。

    b、ECMAScript6,Webpack,NPM以及Vue-Cli的基本用法,最好对Node.js也要有所了解

    然后开始学习ECMAScript6,Webpack,NPM以及Vue-Cli的基本用法,最好对Node.js也要有所了解。

    c、看网上各种实战视频以及文章还有别人开源的源代码

    最后组件部分先大致看一遍,了解组件里面都有哪些概念之后,开始看网上各种实战视频以及文章还有别人开源的源代码。

     

    14、ECMAScript是啥?

    ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。日常场合,这两个词是可以互换的。

    而ECMAScript6就是新一代的JavaScript语言。

     

    15、Webpack是啥?

    a、前端打包工具

    Webpack是一个前端打包和构建工具。如果你之前一直是手写HTML,CSS,JavaScript,并且通过link标签将CSS引入你的HTML文件,以及通过Script标签的src属性引入外部的JS脚本,那么你肯定会对这个工具感到陌生。不要紧,我们先来了解一下为什么要用Webpack,然后带着原因去学习就好了。

     

    16、为什么要用Webpack?

    a、方便管理各种素材

    b、打包以便减少浏览器的访问次数

    前面说了,做一个单页应用程序本身就相当复杂,而且在做的时候肯定会使用到很多素材和别的第三方库,我们该如何去管理这些东西呢?

    还有前面讲到了Webpack是一个前端打包工具,前端代码为什么要打包呢?因为单页应用程序中用到很多素材,如果每一个素材都通过在HTML中以src属性或者link来引入,那么请求一个页面的时候,可能浏览器就要发起十多次请求,往往请求的这些资源都是一些脚本代码或者很小的图片,这些资源本身才几k,下载连1秒都不需要,但是由于HTTP是应用层协议,它的下层是TCP这个运输层协议,TCP的握手和挥手过程消耗的时间可能比下载资源本身还要长,所以需要把这些小文件全部打包成一个文件,这样只要一次TCP握手和挥手的过程,就把多个资源给下载下来了,并且多个资源由于都是共享一个HTTP请求,所以head等部分也是共享的,相当于形成了规模效应,让网页展现更快,用户体验更好。

     

    17、NPM和Node.js又是什么?它们是什么关系?

    a、Node.js是一个服务端的JavaScript运行环境

    Node.js是一个服务端的JavaScript运行环境,通过Node.js可以实现用JavaScript写独立程序。

    b、Node.js可以写独立程序(Webpack就是Node.js写的)

    像我们之前提到的Webpack就是Node.js写的,所以作为一个前端开发,即使你不用Node.js写独立程序,也得配一个Node.js运行环境,毕竟很多前端工具都是使用它写的。

    c、NPM是一个node.js的包管理器(类似java的maven(包的依赖管理),php也有一个类似的)。

    NPM是一个node.js的包管理器。我们在传统开发的时候,JQuery.js大多都是百度搜索,然后去官网下载,或者直接引入CDN资源,这种方法太过于麻烦。如果以后遇到其他的包,这个包的代码本身可能还调用了其他的包(也称这个包和其他的那几个包存在依赖关系),那么我们要在自己的项目中引入一个包将变得十分困难。现在我们有了NPM这个包管理器,直接可以通过

    npm install xxx包名称
    

    的方式引入它,比如说

    npm install vue

     

    18、Vue-CLi是啥?

    它是一个vue.js的脚手架工具。说白了就是一个自动帮你生成好项目目录,配置好Webpack,以及各种依赖包的工具,它可以通过

    npm install vue-cli -g
    

    的方式安装,后面的-g表示全局安装的意思,意味着你可以打开命令行之后直接通过vue命令调用它。

     

    19、Vuex和Vue-route是什么(暂时了解即可)?

    Vuex是vue的一个状态管理器。用于集中管理一个单页应用程序中的各种状态。

    Vue-route是vue的一个前端路由器,这个路由器不是我们上网用的路由器,而是一个管理请求入口和页面映射关系的东西。它可以实现对页面局部进行无刷新的替换,让用户感觉就像切换到了网页一样。

    要讲清楚这两个东西,又得花费大量篇幅,所以这里只是简单提一下,先学好vue.js本身才是最重要的。

     

     

     

     

    二、vue.js到底有什么用

    1、问题

    最近在看vue,但是不懂的是假设说我要用vue做一个练手的小项目,该仿照什么去做呢,比如说商城类的网站,是所有功能模块都可以用vue做,还是只是购物车那块儿适合用vue,还有查了很多资料都说vue适合移动端开发,有没有大神可以举个例子,哪些手机APP用到了vue?为什么要用vue,vue到底该用在哪儿?就是说即使我懂一点vue,我也不知道该拿它来干点什么正事,只能写一些小功能,该如何把vue运用到一个项目中?

     

    2、解答

    vue可以简单写单个页面,也可以写一个大的前端系统,个人建议如果写小的页面(零基础学习vue的话)可以看看vue官网提供样例:https://github.com/vuejs/vue/...,如果稍微大点的项目,推荐看看https://github.com/bailicangd...这个项目。
    换一个角度来收,你可以直接用vue.js写一个页面,而不用jQuery来搞事(简单页面)

     

    Vue可以做从简单到复杂的前端单页应用,随处可见的Web前端都可以用Vue来开发。

    而且Vue上手速度快、功能强大,且提供了非常好用的脚手架vue-cli,很简单就可以构建并让自己的项目跑起来。

    刚学Vue的时候做了一个简单的练手项目,https://github.com/answershut...,类似市面上的易企秀,根据用户设计产生精美的页面。

     

    这个是我学习angularjs1.x练手的项目,一个HTML5播放器,使用了数据绑定思想。你可参考下
    http://ngdemo.sinaapp.com/audio/

     

    三、什么是vue.js?

    Vue.js新手入门指南

    最近在逛各大网站,论坛,以及像SegmentFault等编程问答社区,发现Vue.js异常火爆,重复性的提问和内容也很多,楼主自己也趁着这个大前端的热潮,着手学习了一段时间的Vue.js,目前用它正在做自己的结业项目。

    在做的过程中也对Vue.js的官方文档以及其各种特性有了许多认识。作为一个之前以PHP+模版引擎为主的开发,从一个从未接触过除HTML+CSS+JavaScript+jQuery以外的前端技术的人到现在可以独立使用Vue.js以及各种附属的UI库来开发项目,我总结了一些知识和经验想与大家分享。

    下面我就以问答的形式来分享吧。这里假设你仅仅只掌握了HTML+CSS+JavaScript,如果你对JQuery这个前端库,以及各种后端模版语言比如说PHP,JSP还有所了解并且使用过的话那就太好了。

    1.Vue.js是什么?

    Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue 的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue 生态系统支持的库开发的复杂单页应用。

    Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    如果你是有经验的前端开发者,想知道 Vue.js 与其它库/框架的区别,查看对比其它框架

    这是官网的介绍,是不是觉得非常的抽象非常的官方?看完之后可能还是有很多人不是很懂这个框架到底是用来做什么的,什么是“渐进式框架”?什么是“自底向上增量开发”?什么是“视图层”?什么是“单文件组件”?什么是“复杂的单页应用?”第二段话里面“响应的数据绑定和组合的视图组件”这又是个啥?还有最后一段话,“Vue.js 与其它库/框架的区别”究竟是什么?

    不要担心,如果你慢慢看完这里面的所有问答,一定会对前面那些可能你从未听说过的专业术语有一种恍然大悟的感觉。

    2.Vue.js到底是什么?

    想必现在能看到我这篇文章的人,都是用着APP或者网页版知乎在阅读把。Vue.js就是一个用于搭建类似于网页版知乎这种表单项繁多,且内容需要根据用户的操作进行修改的网页版应用。

    3.单页应用程序(SPA)

    顾名思义,单页应用一般指的就是一个页面就是应用,当然也可以是一个子应用,比如说知乎的一个页面就可以视为一个子应用。单页应用程序中一般交互处理非常多,而且页面中的内容需要根据用户的操作动态变化。

    4.你前面说的网页版知乎我也可以用JQuery写啊,为什么要用Vue.js呢?

    讲到JQuery,就不得不说到JavaScript的DOM操作了。如果你用JQuery来开发一个知乎,那么你就需要用JQuery中的各种DOM操作方法去操作HTML的DOM结构了。

    现在我们把一个网页应用抽象一下,那么HTML中的DOM其实就是视图,一个网页就是通过DOM的组合与嵌套,形成了最基本的视图结构,再通过CSS的修饰,在基本的视图结构上“化妆”让他们看起来更加美观。最后涉及到交互部分,就需要用到JavaScript来接受用户的交互请求,并且通过事件机制来响应用户的交互操作,并且在事件的处理函数中进行各种数据的修改,比如说修改某个DOM中的innerHTML或者innerText部分。

    我们把HTML中的DOM就可以与其他的部分独立开来划分出一个层次,这个层次就叫做视图层

    Vue 的核心库只关注视图层

    我们为什么要把视图层抽取出来并且单独去关注它呢?

    因为在像知乎这种页面元素非常多,结构很庞大的网页中,数据和视图如果全部混杂在一起,像传统开发一样全部混合在HTML中,那么要对它们进行处理会十分的费劲,并且如果其中有几个结构之间存在藕断丝连的关系,那么会导致代码上出现更大的问题,这什么问题呢?

    你是否还记得你当初写JQuery的时候,有写过('#xxx').parent().parent().parent()这种代码呢?当你第一次写的时候,你觉得页面元素不多,不就是找这个元素的爸爸的爸爸的爸爸吗,我大不了在注释里面写清楚这个元素的爸爸的爸爸的爸爸不就好了。但是万一过几天之后你的项目组长或者你的产品经理突然对你做的网页提出修改要求,这个修改要求将会影响页面的结构,也就是DOM的关联与嵌套层次要发生改变,那么 (‘#xxx’).parent().parent().parent()可能就会变成$(‘#xxx’).parent().parent().parent().parent().parent()了。

    这还不算什么,等以后产品迭代越来越快,修改越来越多,而且页面中类似的关联和嵌套DOM元素不止一个,那么修改起来将非常费劲。而且JQuery选择器查找页面元素以及DOM操作本身也是有性能损失的,可能到时候打开这个页面,会变得越来越卡,而你却无从下手。

    当你在编写项目的时候遇到了这种问题,你一定会抱怨,为什么世上会有HTML这种像盗梦空间一样的需要无数div嵌套才能做出页面的语言,为什么当初学JQuery看中的是它简洁的DOM操作,现在却一点也不觉得它有多简洁,难道我学的是假的JQuery?为什么写个代码这么难,你想砸电脑,你想一键盘拍在产品狗的脑袋上,责怪他天天改需求才让你原本花清香茶清味的代码变得如此又臭又长。

    这个时候如果你学过Vue.js,那么这些抱怨将不复存在。

    5.Vue.js为什么能让基于网页的前端应用程序开发起来这么方便?

    因为Vue.js有声明式,响应式的数据绑定,与组件化的开发,并且还使用了Virtual DOM这个看名字就觉得高大上的技术。

    可是这些名词都是啥?

    6.响应式的数据绑定

    这里的响应式不是@media 媒体查询中的响应式布局,而是指vue.js会自动对页面中某些数据的变化做出响应。至于是如何响应的,大家可以先把下面这段代码随便粘贴到一个扩展名为html的文件然后用浏览器打开,随便在文本框里面输入一些文字,观察一下页面变化。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>vue.js测试 - 代码之美专栏</title>
        <!-- author:昌维 代码之美 https://zhuanlan.zhihu.com/codes -->
        <script src="https://unpkg.com/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <input type="text" name="" value="" placeholder="在这里输入文字,下面会跟着变化" v-model="message">
            <hr>
            <p>{{ message }}</p>
        </div>
        <script type="text/javascript">
            var app = new Vue({
              el: '#app',
              data: {
                message: 'Hello Vue!'
              }
            })
        </script>
    </body>
    </html>
    

    是不是会发现一个很神奇的现象,文本框里面输入的文字和后面的p标签中的内容一起变化?

    换句话说,p标签里面通过{{ message }}这个写法与input标签中的value绑定在了一起,其中变化,另外一个和它绑定的数据就跟着变化。

    结合标题来说,就是vue.js会自动响应数据的变化情况,并且根据用户在代码中预先写好的绑定关系,对所有绑定在一起的数据和视图内容都进行修改。而这种绑定关系,在图上是以input 标签的v-model属性来声明的,因此你在别的地方可能也会看到有人粗略的称vue.js为声明式渲染的模版引擎。

    7.组件化开发

    还记得在传统前端开发的时候,我们都是每个人做一个页面,然后最后套入各种后端模版引擎,比如说PHP的Smarty或者Java的JSP等等。

    但是现在我们做单页应用,页面交互和结构十分复杂,一个页面上就有许许多多的模块需要编写,而且往往一个模块的代码量和工作量就非常庞大,如果还按照原先的方法来开发,那么会累死人。而且遇到以后的产品需求变更,修改起来也非常麻烦,生怕动了其中一个div之后,其他div跟着雪崩,整个页面全部乱套,或者由于JavaScript的事件冒泡机制,导致修改一些内层的DOM事件处理函数之后,出现各种莫名其妙的诡异BUG。

    在面向对象编程中,我们可以使用面向对象的思想将各种模块打包成类或者把一个大的业务模块拆分成更多更小的几个类。在面向过程编程中,我们也可以把一些大功能拆分成许多函数,然后分配给不同的人来开发。

    在前端应用,我们是否也可以像编程一样把模块封装呢?这就引入了组件化开发的思想。

    Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件(component)中,我们只要先在父级应用中写好各种组件标签(占坑),并且在组件标签中写好要传入组件的参数(就像给函数传入参数一样,这个参数叫做组件的属性),然后再分别写好各种组件的实现(填坑),然后整个应用就算做完了。

    8.Virtual DOM

    现在的网速越来越快了,很多人家里都是几十甚至上百M的光纤,手机也是4G起步了,按道理一个网页才几百K,而且浏览器本身还会缓存很多资源文件,那么几十M的光纤为什么打开一个之前已经打开过,已经有缓存的页面还是感觉很慢呢?这就是因为浏览器本身处理DOM也是有性能瓶颈的,尤其是在传统开发中,用JQuery或者原生的JavaScript DOM操作函数对DOM进行频繁操作的时候,浏览器要不停的渲染新的DOM树,导致页面看起来非常卡顿。

    而Virtual DOM则是虚拟DOM的英文,简单来说,他就是一种可以预先通过JavaScript进行各种计算,把最终的DOM操作计算出来并优化,由于这个DOM操作属于预处理操作,并没有真实的操作DOM,所以叫做虚拟DOM。最后在计算完毕才真正将DOM操作提交,将DOM操作变化反映到DOM树上。

    对于vue.js的Virtual DOM,目前业界有着褒贬不一的评价。有人认为Vue.js作为一个轻量级框架,引入Virtual DOM会加大Vue.js本身的代码尺寸,也会消耗更多CPU(手机上会更耗电)(注意:消耗更多的CPU并不意味着会更卡,因为JavaScript计算是后台计算,他的计算量还不至于让DOM操作变得卡顿),并且在操作单个DOM元素的时候,反而多了一道计算工序,会更慢。但也有人认为基本上会用Vue.js开发的都是页面中内容很多的元素,肯定操作的DOM量级普遍较大,平均一下还是比较划算的。

    9.我到底该怎么用Vue.js做单页应用开发?

    说了这么多,我还是不知道怎么用它做出一个像知乎那样的页面啊,到底怎么学它呢?

    前面我们看了一个响应式的数据绑定案例,那只是一个DEMO,而且也看不出有什么实际意义,离真正的单页应用程序还差得远,到底怎么用它开发真实的项目呢?

    我的建议是,先把介绍 - vue.js官方文档的基础部分硬着头皮看一遍。除了组件这个小节涉及到了很多晦涩难懂的名词以外,前面几章完全就是把Vue.js当作一个模版引擎来用。

    然后开始学习ECMAScript6,Webpack,NPM以及Vue-Cli的基本用法,最好对Node.js也要有所了解。

    最后组件部分先大致看一遍,了解组件里面都有哪些概念之后,开始看网上各种实战视频以及文章还有别人开源的源代码。

    10.在前面你提到过好几次ECMAScript,这是啥?

    ECMAScript听名字好像和JavaScript很像,难不成他们有什么千丝万缕的联系?

    没错你猜对了,他们之间还真有着很深的联系。

    引用阮一峰老师的话:(ECMAScript 6入门

    要讲清楚这个问题,需要回顾历史。1996年11月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给国际标准化组织ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是1.0版。

    该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

    因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。日常场合,这两个词是可以互换的。

    而ECMAScript6就是新一代的JavaScript语言。

    这里也强烈推荐大家学习ECMAScript6的时候参考这本书ECMAScript 6入门

    11.我在学习Vue.js的时候老是听到Webpack,这是啥?

    Webpack是一个前端打包和构建工具。如果你之前一直是手写HTML,CSS,JavaScript,并且通过link标签将CSS引入你的HTML文件,以及通过Script标签的src属性引入外部的JS脚本,那么你肯定会对这个工具感到陌生。不要紧,我们先来了解一下为什么要用Webpack,然后带着原因去学习就好了。

    12.为什么要用Webpack

    前面说了,做一个单页应用程序本身就相当复杂,而且在做的时候肯定会使用到很多素材和别的第三方库,我们该如何去管理这些东西呢?

    还有前面讲到了Webpack是一个前端打包工具,前端代码为什么要打包呢?因为单页应用程序中用到很多素材,如果每一个素材都通过在HTML中以src属性或者link来引入,那么请求一个页面的时候,可能浏览器就要发起十多次请求,往往请求的这些资源都是一些脚本代码或者很小的图片,这些资源本身才几k,下载连1秒都不需要,但是由于HTTP是应用层协议,它的下层是TCP这个运输层协议,TCP的握手和挥手过程消耗的时间可能比下载资源本身还要长,所以需要把这些小文件全部打包成一个文件,这样只要一次TCP握手和挥手的过程,就把多个资源给下载下来了,并且多个资源由于都是共享一个HTTP请求,所以head等部分也是共享的,相当于形成了规模效应,让网页展现更快,用户体验更好。

    前面说到Webpack还有构建的功能,这就不得不提到了ECMAScript6这个新版本的JavaScript,但是现在国内外还有很多人用着老版本的浏览器,这些浏览器并不支持ECMAScript6,那么我们的前端项目如何在这种浏览器上运行呢?这就需要Webpack的Loader自动载入一个转换器来将我们写的ECMAScript6转换成浏览器能支持的老版本JavaScript语言,这个转换器的名字叫做babel,如果你以后听到或者看到了这个单词,应该要知道它就是一个ECMAScript6 to 老版本JavaScript的转换器了。这也是Webpack的构建功能。当然对前端有更深入的同学还会知道Sass,Less,stylus之类的CSS预处理器,我们也可以通过在Loader中编写特定的规则来实现自动将这些CSS预处理语言转换成普通浏览器能识别的CSS代码。

    开头的介绍提到了vue.js可以使用单文件组件开发项目,其实也是通过Webpack将单文件组件中的模版,样式以及JS转换到主页面中

    当然Webpack不止这点功能,它还可以通过安装各种插件来扩展,比如说热加载技术,就是解放键盘的F5键。让我们修改代码,并且按Ctrl+S保存之后,浏览器页面自动刷新变化,不需要我们去手动刷新,还有一些插件可以自动添加注释,自动给CSS代码加上一些浏览器内核对CSS3兼容前缀,就像webkit-xxx之类的一样。

    13.NPM和Node.js又是什么?它们是什么关系?

    首先讲讲node.js。我们知道通常情况下,JavaScript的运行环境都是浏览器,因此JavaScript的能力也就局限于浏览器能赋予它的权限了。比如说读写本地系统文件这种操作,一般情况下运行在浏览器中的JavaScript代码是没有这个操作权限的。如果我们想用JavaScript写出一些能够运行在操作系统上的,能够具有像PHP,JAVA之类的编程语言具有的功能的程序该怎么办呢?Node.js就解决了这个问题。Node.js是一个服务端的JavaScript运行环境,通过Node.js可以实现用JavaScript写独立程序。像我们之前提到的Webpack就是Node.js写的,所以作为一个前端开发,即使你不用Node.js写独立程序,也得配一个Node.js运行环境,毕竟很多前端工具都是使用它写的。

    NPM是一个node.js的包管理器。我们在传统开发的时候,JQuery.js大多都是百度搜索,然后去官网下载,或者直接引入CDN资源,这种方法太过于麻烦。如果以后遇到其他的包,这个包的代码本身可能还调用了其他的包(也称这个包和其他的那几个包存在依赖关系),那么我们要在自己的项目中引入一个包将变得十分困难。现在我们有了NPM这个包管理器,直接可以通过

    npm install xxx包名称
    

    的方式引入它,比如说

    npm install vue
    

    就自动在当前项目文件夹下导入了这个包,并且npm自动下载好了vue这个包依赖的其他包。

    至于有的人在按照网上的npm教程配置的时候踩坑了,发现下载速度很慢或者完全下载不了,那是因为我国有着众所周知的原因,网上也有各种解决方法可以解决这个问题,大家多善用搜索引擎

    前面提到了Webpack可以安装各种插件来扩展功能,其实也是通过这种方式扩展。

    如果你学过PHP的话,NPM就和PHP里面的Composer差不多。也和CentOS下的yum和Ubuntu下的apt-get差不多。

    14.Vue-CLi又是啥?

    它是一个vue.js的脚手架工具。说白了就是一个自动帮你生成好项目目录,配置好Webpack,以及各种依赖包的工具,它可以通过

    npm install vue-cli -g
    

    的方式安装,后面的-g表示全局安装的意思,意味着你可以打开命令行之后直接通过vue命令调用它。

    15.我该不该学Vue.js?

    现在Vue.js无论是发展势头还是作者支持还是很好的,而且它本身中文资料就比较多,教程也很多,现在随随便便打开几个和前端开发有关的知乎专栏,基本上都能见到相关文章,社区也很活跃。

    至于你该不该学,取决于你自己,如果你当前只是做做以内容展示为主的项目,或者就是成天用各种CMS建站仿站,并且以后都不打算更换更好的工作,那么可以暂时不用学。如果你开发的项目交互非常多,而且前后端开发都对前后端分离有很清楚的认识,那么可以大胆的学习,并且在实际项目中运用。

    16.Vue.js怎么火起来的?

    关于这个问题,网上说法很多,我自己认为主要还是前些年大前端变革太快,而最近一年开始Vue.js+Webpack这个组合开始逐渐稳定下来了,而且已经有了很多中文资料。

    对比它的竞争对手AngularJS,新旧版本项目无法平滑升级,变革太大让用户感觉不安稳。

    React本身主流推荐用的是JSX,需要额外学习一门语法(什么?学Vue.js还要学ECMAScript6?现在ECMAScript6是趋势,并不是因为Vue.js才要学的),并且React本身用的是render写法编写模版代码,这让很多用习惯了Smarty等后端模版引擎得人来使用感觉很不适应,现在看来React本身在中国一些论坛社区的火爆程度还是没有Vue.js高。

    当然也并不是说除了Vue.js以外其他框架都很差。像知乎新版也是用React开发的,他还是有各自优秀的地方大家可以深入学习之后做出自己的判断。

    17.我在很多地方还看到Vuex和Vue-route,它们又是什么?

    Vuex是vue的一个状态管理器。用于集中管理一个单页应用程序中的各种状态。

    Vue-route是vue的一个前端路由器,这个路由器不是我们上网用的路由器,而是一个管理请求入口和页面映射关系的东西。它可以实现对页面局部进行无刷新的替换,让用户感觉就像切换到了网页一样。

    要讲清楚这两个东西,又得花费大量篇幅,所以这里只是简单提一下,先学好vue.js本身才是最重要的。

    18.我还在一些地方看到过Vue-resource和Axios,它们又是什么?

    我们在传统的前后端不分离的开发中,后端直接把数据通过模版引擎拼接进了返回的HTML中。而现在做单页应用程序属于前后端分离开发,那么这个单页应用程序中的数据就得通过ajax的方式获取,也要通过ajax的方式提交到后端。

    在传统开发中我们都是通过xmlhttprequest手动操作,或者通过JQuery的ajax方法来进行数据提交获取。

    vue.js本身没有封装ajax操作库,所以我们要通过Vue-resource和Axios来进行ajax操作,而因为种种原因,现在vue.js2.0已经将axios作为官方推荐的ajax库了。

    19.我该学Vue.js2.0还是1.0呢?

    现在很多框架和语言都是学新不学旧(Python慢慢也变得如此),因此如果不是为了维护老旧项目,肯定推荐学Vue.js2.0。而且学会了Vue.js2.0,再去学习Vue.js1.0也不是什么难事。

    20.写Vue.js用什么开发工具和电脑呢?

    前端开发基本上不需要太高端的电脑都能胜任,现在这个时代是台电脑,装个编辑器都可以做前端开发的。

    Vue.js的组件文件本质上还是普通的代码文件,所以各种编辑器配合一些语法检查插件就足够了。我自己用的是sublime text 3,安装一些插件之后可以实现.vue单文件组件高亮代码以及各种自动完成。Webstorm中也有类似插件,大家可以在网上各种教学文章的指导下配置好环境。

    我用的主题是Monokai,这个主题在大部分显示器下长时间观看眼睛不会很疼。我使用的显示器是LG IPS236,部分显示器有亮度色标调节,我这款显示器调到2.0左右会比较舒适,过低会导致底部的茶色背景变黑,反差较大更加疲劳,过高会导致对比度不足,画面泛白影响调色。

    结语:

    可能包括我在内的很多人在看到Vue.js那神奇的双向绑定优雅写法都会有一种非常惊艳,而看到中文文档发布之后有种想立马学习的冲动。可惜大前端终究是大前端,如果一个对于前端各个方面没有深入认识就想着能一步登天,肯定对不起大前端的“大”字。原本看着只想学一个Vue.js,没想到顺带把ECMAScript6,Webpack配置,ESLint配置,bable配置,npm使用,node.js语法,以后vue全家桶中的vuex,vue-route等等等等都学了一遍。前段时间网上也流传出了一个职位叫做Webpack配置工程师,从这里也可以看出弄懂前端这个大杂烩确实不是那么容易。大家一起加油,有什么问题也可以在评论区回复,我会抽空补充在文章内容中。谢谢各位的支持!~

     

     

    参考:https://blog.csdn.net/wxl1555/article/details/79964818

     

     

    展开全文
  • Vue.js初学

    2020-11-19 16:57:49
    Vue.js从零开始 1、简单认识Vue.js Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,...

    Vue.js从零开始

    1、简单认识Vue.js

    Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

    2、Vue.js安装

    1. CDN引入
    2. 直接下载然后引入,script方式引用
    3. npm方式安装

    注意开发环境版本(vue.js)和生产环境版本(vue.min.js)的区别:开发环境版本有所有的js代码,包括注释,警告信息,用于开发的时候用,体积比较大;生产环境版本就是项目上线的时候用,所有代码整合成了一行,去掉了注释和警告信息,用于项目实际上线的时候用,体积比较小。

    3、第一个Vue.js程序

    我用的是编译器是IDEA,所以我只需要在plugins市场下载vue插件,然后重启IDEA创建一个空项目(empty project),然后新建一个文件夹名叫js,把刚才下载好的vue.js放到这个文件夹里。然后再html里面引用就可以了。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <!--引入vue.js-->
        <script src="../js/vue.js"></script>
        <meta charset="UTF-8">
        <title>vue.js第一课</title>
    </head>
    <body>
    <!--定义一个id为app的盒子交给vue接管-->
    <div id="app">{{message}}{{message2}}</div>
    </body>
    <script>
        //实例化Vue对象
        let app = new Vue({
            //el是element(元素)的缩写,接管id为app的盒子,id选择器
            el: '#app',
            //定义数据
            data: {
                //上文中用{{message}}取得数据
                message: 'hello,',
                message2: 'Vue.js',
            }
        });
    </script>
    </html>
    

    步骤总结:

    1. 用script的方式引入vue.js
    2. 创建id为app(自定义)的div盒子
    3. 再写script代码
    4. 先new一个Vue对象,参数是el(上文的元素),data(定义数据),名叫app(自定义)接管上文的id叫app的盒子,并且定义数据
    5. 最后在盒子里面用{{数据名}}就可以获取到定义的数据了
    6. data里的数据可以直接定义,也可以从后端或者服务器加载

    4、循环v-for

    尝试一下循环列表,我们先在data里面定义一个数组[]是数组,{}是对象

    <!--定义一个id为app的盒子交给vue接管-->
    <div id="app">
        <ul>
            <!--类似Java的增强for循环,students是集合,student是集合中的单个元素-->
            <li v-for="student in students">{{student}}</li>
        </ul>
    </div>
    </body>
    <script>
        //实例化Vue对象
        let app = new Vue({
            //el是element(元素)的缩写,接管id为app的盒子,id选择器
            el: '#app',
            //定义数据
            data: {
                students: ['张三','李四','王麻子']
            }
        });
    </script>
    

    利用v-for="item in items"的方式遍历中items集合中的单个item元素(类似Java中的增强for循环),然后再利用{{item}}取出集合中的单个元素,同时还可以在控制台利用app.students.pop()去掉最后一个数据

    5、事件v-on

    在vue中可以使用v-on:事件名="方法名(参数列表)"来调用事件,比如调用单击事件就用v-on:click,双击事件就用v-on:dblclick等等其他事件,同时也可以简写成@click或者@dblclick

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <!--引入vue.js-->
        <script src="../js/vue.js"></script>
        <meta charset="UTF-8">
        <title>事件</title>
    </head>
    <body>
    <!--指定一个id为app的div盒子-->
    <div id="app">
        <h3>计数器:{{counter}}</h3>
        <!--调用increment函数-->
        <button v-on:click="increment()">+</button>
        <!--调用decrement函数-->
        <button v-on:click="decrement()">-</button>
    </div>
    <script>
        //实例化Vue对象
        let app = new Vue({
            //获得id为app的div盒子
            el: '#app',
            //定义数据
            data: {
                //计数器
                counter: 0,
            },
            //定义方法
            methods: {
                //递增方法
                increment(){
                    this.counter++;
                    console.log('调用了increment方法');
                },
                //递减方法
                decrement(id){
                    this.counter--;
                    console.log('调用了decrement方法');
                }
            }
        });
    </script>
    </body>
    </html>
    

    需要注意的是必须在实例化的Vue对象里添加methods属性,然后再在methods属性里添加方法。

    6、Vue的MVVM模式

    MVVM分为三个部分:

    M(Model,模型层):主要负责业务数据相关;

    V(View,视图层):负责视图相关,比如网页的Dom

    VM(ViewModel,Model层和View层的桥梁):负责监听M然后对V进行修改,实现M和V的双向绑定。当M层的数据发生修改时,VM层会监测到变化然后立马对V层的数据也进行修改。反之,当V层的数据发生修改时,VM层也会监测到变化然后对M层的数据进行修改。

    总的来说就是Model层和View不会直接相互操作,然后通过中间的一层VM层进行互相联系,降低了耦合。

    而且Vue中大部分时间中Vue就是充当了VM层,我们作为程序员只需要做好View层和Model层就好了,因为Vue已经帮我们自动监听加解析加修改。

    7、Vue的生命周期

    和微信小程序开发类似,在Vue.js里每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。同时生命周期的钩子函数是通过Vue实例的options对象的属性值传递给Vue的。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Vue.js的生命周期</title>
        <script src="../js/vue.js"></script>
    </head>
    <body>
    <div id="app">{{message}}</div>
    <script>
        let app = new Vue({
            //绑定上文的元素
            el: '#app',
            //定义数据
            data: {
               message: 'Hello,Vue.js!'
            },
            //定义方法
            methods:{
    
            },
            //生命周期函数:created(Vue实例被创建后运行的函数)
            created:function () {
                console.log('lifeCycle--->created函数执行');
            }
        });
    </script>
    </body>
    </html>
    

    也有一些其它的钩子,在实例生命周期的不同阶段被调用,如mountedupdateddestroyed生命周期钩子的 this 上下文指向调用它的 Vue 实例。

    这是Vue实例的生命周期图示:

    红线标注的都是执行到对应的生命周期的时候会调用的生命周期钩子函数

    在这里插入图片描述

    8、Vue.js的插值操作

    8.1 v-once

    有的时候可能需要View的数据不根据Model的数据动态变化,就可以使用v-once指令。

    <body>
    
    <div id="app">
      <h2>{{message}}</h2>
      <h2 v-once>{{message}}</h2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
        },
      });
    </script>
    
    </body>
    

    加了v-once后{{message}}只会被渲染一次,后面model再改变message的数据的话依旧显示的是第一次渲染的数据不会发生动态改变。就像常量一样,加了v-once标签的数据不再发生改变。

    8.2 v-html

    有的时候可能后端向前端传递的数据不是纯文本或者纯字符串而是富文本,比如<h2>标题</h2>,我们希望它在页面上显示的是标题而不是<h2>标题</h2>,这个时候我们就可以给这个标签加上v-html指令

    <body>
    
    <div id="app">
      <p>{{h2}}</p>
      <p v-html="h2">}</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
          h2: '<h2>标题</h2>',
        },
      });
    </script>
    
    </body>
    

    运行结果:

    在这里插入图片描述

    可以看到没有加上v-html指令的标签内显示的内容就是纯字符串,加上了v-html指令的标签内显示的内容就是经过解析过后的富文本

    8.3 v-text

    <body>
    
    <div id="app">
      <p>{{text}},Vue.js!</p>
      <p v-text="text">,Vue.js!</p>
      <p v-text="text+',Vue.js!'">,Vue.js!</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
          text: 'Hello',
        },
      });
    </script>
    
    </body>
    

    运行结果:

    在这里插入图片描述

    可以观察到运行结果,如果直接使用Mustache语法({{}}双大括号)可以直接在标签内里进行拼接字符串,但是如果使用了v-text指令后会直接将标签里的内容覆盖掉,但是同时也可以在v-text指令里使用+号进行拼接。

    8.4 v-pre

    <body>
    
    <div id="app">
      <p>{{message}}</p>
      <p v-pre>{{message}}</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
        },
      });
    </script>
    
    </body>
    

    运行结果:

    在这里插入图片描述

    v-pre就是实打实的显示标签内的内容而不经过解析。

    9、v-bind动态绑定

    9.1 v-bind的基本使用

    <body>
    
    <div id="app">
      <img v-bind:src="imgUrl" alt="">
      <a v-bind:href="aHref">百度一下</a>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          aHref: 'https://www.baidu.com',
          imgUrl: 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
        },
      });
    </script>
    
    </body>
    

    传统的img和a标签里的url都是直接写死的,但是开发中肯定不会写死,肯定是从服务器请求然后得到JSON字符串然后从中获取到对应的url存储到Model里面,这个时候就需要用到v-bind指令来将Model里面的url放到img或者a标签里,使得它们能够解析url。

    同时v-bind还有一个语法糖(简写):

    常规写法:

    <!--常规写法-->
      <img v-bind:src="imgUrl" alt="">
      <a v-bind:href="aHref">百度一下</a>
    

    语法糖:(直接将v-bind:替换成:

    <!--语法糖(简写)-->
      <img :src="imgUrl" alt="">
      <a :href="aHref">百度一下</a>
    

    9.2 v-bind动态绑定class(对象语法)

    在vue里面可以实现动态绑定class,并且可以在class里面定义对象,对象里面采用key-value键值对的方式来确定这个属性是否生效(value必须是boolean类型),同时原来已有的class属性不会被覆盖,而是合并。

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>v-bind动态绑定class(对象语法)</title>
      <style>
        .active1{
          color: red;
        }
        .active2{
          color: blue;
        }
      </style>
    </head>
    <body>
    
    <div id="app">
      <h2 class="line" :class="{'active1': isActive1,'active2': isActive2}">{{message}}</h2>
      <button v-on:click="changeColor()">切换颜色</button>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: '变色',
          isActive1: true,
          isActive2: false,
        },
        methods: {
          changeColor() {
            this.isActive1=!this.isActive1;
            this.isActive2=!this.isActive2;
          }
        },
      });
    </script>
    
    </body>
    </html>
    

    h2标签里原本有一个固定的class属性叫line,同时后面交给vue解析的里面有一个对象,对象采用的是键值对的方式存储的,只要某个键(属性)的值为true,那么这个属性就会被启用,否则就不会被启用。

    总结就是class属性可以用原本固定的,不会被覆盖,只会被合并,:class属性里面可以使用对象来存储,对象里面是键值对的方式存储,key-value,如果value为true则该属性就会被启用,否则不会被启用。

    9.3 v-bind动态绑定style(对象语法)

    v-bind指令也可以动态绑定style的样式:直接在style里利用属性名:属性值给style赋值:

    <body>
    
    <div id="app">
      <!--v-bind动态绑定style格式
      <p :style="{key(属性名):value(属性值)}"></p>-->
      <!--直接赋值-->
      <p :style="{color:'red','fontSize':'50px'}">{{message}}</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
        },
      });
    </script>
    
    </body>
    

    可以看到在style里依然是利用的对象语法来赋值的,key:value,假如value写死了的话就需要加上''单引号。

    也可以动态获取后端传的value:

    <body>
    
    <div id="app">
      <!--v-bind动态绑定style格式
      <p :style="{key(属性名):value(属性值)}"></p>-->
      <!--动态绑定下面的值-->
      <p :style="{'color':color,'fontSize':fontSize}">{{message}}</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
          color: 'red',
          fontSize: '50px',
        },
      });
    </script>
    
    </body>
    

    记住,直接赋值的时候的value应该加单引号,而动态获取值的时候就不要加引号,这样交给Vue解析的时候才会从下面去获取值。

    10、computed计算属性

    computed(计算好的,已经计算的)的属性就叫计算属性。在这之前我们可以直接在html的标签里使用Mustache语法使用data里的数据。像这样:

    <body>
    
    <div id="app">
      <!--利用Mustache语法获取data里面的数据-->
      <p>{{firstName}} {{lastName}}</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          firstName: 'James',
          lastName: 'Bond'
        },
      });
    </script>
    
    </body>
    

    但是有的时候在Mustache语法里无法实现我们想要的效果,所以有的时候我们可以采用计算属性,也就是在Vue实例里添加computed属性,添加类似函数的一样的东西:有函数名,函数体,返回值(返回计算好的属性值)。在上文依旧是采用Mustache语法进行调用:

    <body>
    
    <div id="app">
      <!--利用Mustache语法获取data里面的数据-->
      <p>{{firstName}} {{lastName}}</p>
      <!--利用Mustache语法获取computed里的数据-->
      <p>{{fullName}}</p>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          firstName: 'James',
          lastName: 'Bond'
        },
        //计算属性
        computed: {
          fullName() {
            //返回字符串的组合
            return this.firstName+' '+this.lastName;
          }
        },
      });
    </script>
    
    </body>
    

    为什么要用computed计算属性还有一个原因就是computed里面的属性是有缓存的,是可以提高运行效率的。

    11、computed计算属性的getter和setter

    其实计算属性有更全面的一种写法,依旧是定义computed属性,然后定义属性名,但是值是一个对象,对象里再定义get和set函数,像这样:

    	//计算属性
        computed: {
          fullName: {
            //setter
            set:function () {
    
            },
            //getter
            get:function () {
              return this.firstName+' '+this.lastName;
            }
          }
        },
    

    执行的原理实际上是修改fullName的值的时候就会调用set方法,读取fullName的值的时候就会调用get方法,但是我们一般不会在set方法里面写什么东西,所以就可以直接采用简写:

    	//计算属性
        computed: {
          fullName() {
            return this.firstName+' '+this.lastName;
          }
        },
    

    简写和上面同时显式声明get和set是一样的,同样也都会隐式调用get和set方法。

    12、Vue的语法糖总结

    1. v-bind动态绑定

        <!--v-bind全写-->
        <a v-bind:href="aHref">百度一下</a>
      
        <!--v-bind语法糖(简写)-->
        <a :href="aHref">百度一下</a>
      

      v-bind:直接简写成了:

    2. v-on事件监听

        <!--v-on全写-->
        <button v-on:click="log()">打印</button>
        <!--v-on语法糖(简写)-->
        <button @click="log()">打印</button>
      

      v-on:直接简写成了@

    13、v-on参数传递问题

    v-on绑定的是事件,下面我用click单击事件为例子,梳理一下v-on事件参数传递的问题。

    1. 没有形参

      <body>
      
      <div id="app">
      
        <!--调用方法没有形参的时候可以不加括号(省略写法)-->
        <button @click="btnClick1">按钮1</button>
        <!--调用方法即使没有形参也可以加上括号,跟按钮1是等价的-->
        <button @click="btnClick1()">按钮1</button>
      
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            
          },
          methods: {
            //无参方法
            btnClick1() {
              console.log('btnClick1被调用');
            },
          },
        });
      </script>
      
      </body>
      

      你可以不用加括号或者加上括号都可以,个人建议加上括号,易读。

    2. 有形参

      <body>
      
      <div id="app">
      
        <!--调用方法有形参的时候就可以传递参数-->
        <button @click="btnClick2('测试')">按钮2</button>
      
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
      
          },
          methods: {
            //有参方法
            btnClick2(value){
              console.log('btnClick2被调用');
              console.log(value);
            },
          },
        });
      </script>
      
      </body>
      

      直接作为参数传递进去就好了

    3. 将事件对象作为形参传递

      <body>
      
      <div id="app">
      
        <!--调用方法加上形参并且传递“$event”参数就可以显式传递事件对象(单击)-->
        <button @click="btnClick3($event,'测试')">按钮3</button>
        <!--如果不加上“$event”就会隐式传递事件对象,并且对应第一个形参(不建议这么做)-->
        <button @click="btnClick3">按钮3</button>
      
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
      
          },
          methods: {
            //有参方法,事件对象
            btnClick3(event,value){
              console.log('btnClick3被调用');
              console.log(event);
              console.log(value);
            }
          },
        });
      </script>
      
      </body>
      

      一般的做法就是直接在形参这里写上$event参数,然后就可以直接显式传递给方法自动解析成事件对象,比如click就是单击事件,dblclick就是双击事件。同时还可以直接不传递参数,一个都不写,这样方法里就会自动把第一个实参解析为事件对象,这种是隐式传递(不推荐这样做,不易读)。建议还是加上$event显式传递参数更加直观。

    14、v-on修饰符

    1. .stop修饰符阻止冒泡事件

      给事件加上.stop修饰符可以达到阻止冒泡事件的目的。

      先理解一下冒泡事件的概念:冒泡事件就是事件自下而上(自内而外)依次触发。简单来说在html里面就是假如标签内外都有事件,那么触发了标签最里面的事件后,标签外部的事件也会被触发。举个例子来体会一下:

      <body>
      
      <div id="app">
      
        <div id="div1" @click="divClick">
          <p>div1</p>
          <button @click="btnClick">按钮1</button>
        </div>
      
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            
          },
          methods: {
            //按钮点击事件
            btnClick() {
              console.log('btnClick');
            },
            //div点击事件
            divClick(){
              console.log('divClick');
            },
          },
        });
      </script>
      
      </body>
      

      你会发现,点击按钮不仅会触发divClick()还会触发btnClick()(这就是冒泡事件),点击div1就只会触发divClick()。这在实际开发中并不是我们想要的,我们更多的想要的是点击按钮触发按钮事件,点击div1中文字就触发div事件,所以我们就要阻止这个按钮的冒泡事件传递,那么就只需要在按钮上加上.stop就可以了

      <button @click.stop="btnClick">按钮1</button>
      等价于
      event.stopPropagation()
      

      那么,这个按钮的冒泡事件就被阻止了,就无法触发它外部标签的事件了。

    2. .prevent修饰符阻止组件默认事件

      给事件加上.prevent修饰符以达到禁止该组件的默认事件,比如在form里面的自带的submit提交按钮的默认事件就是向form的action属性发起请求

      <div id="app">
        <form action="https://www.baidu.com">
          <input type="submit" value="提交">
        </form>
      </div>
      

      点击提交按钮就会向https://www.baidu.com发起请求

      如果加上了.prevent修饰符后就会阻止该组件的默认事件了:

      <body>
      
      <div id="app">
        <form action="https://www.baidu.com">
          <!--阻止组件的默认事件,而使用自定义的事件-->
          <input type="submit" value="提交" @click.prevent="submit">
        </form>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
      
          },
          methods: {
            //自定义按钮提交
            submit() {
              console.log('submit');
            }
          },
        });
      </script>
      
      </body>
      

      这样点击按钮后就不会向action发起请求,而是执行用户自定义的submit方法,用户可以在这个方法里面自定义请求。

    3. .[keyCode|keyAlias]修饰符监听键盘具体键位事件

      监听键盘具体某个键的事件。

      <body>
      
      <div id="app">
        <!--输入框(监听键盘抬起事件,所有的按键都会被适用)-->
        <input type="text" @keyup="keyUp">
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            message: 'Hello,Vue.js!',
          },
          methods: {
            //键盘按下后抬起事件
            keyUp() {
              console.log('keyUp');
            }
          },
        });
      </script>
      
      </body>
      

      @keyUp就是监听所有键位的抬起事件,如果加上了具体的某个键位的比如.enter或者.13因为回车键的keyCode就是13,回车键的keyAlias就是enter

      <div id="app">
        <!--输入框(只监听回车键)-->
        <input type="text" @keyup.enter="keyUp">
        <!--输入框(只监听回车键)-->
        <input type="text" @keyup.13="keyUp">
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            message: 'Hello,Vue.js!',
          },
          methods: {
            //键盘按下后抬起事件
            keyUp() {
              console.log('keyUp');
            }
          },
        });
      </script>
      
      </body>
      
    4. .once修饰符只监听一次事件

      只会触发第一次,避免重复触发

      <body>
      
      <div id="app">
        <!--加了.once修饰符后该按钮只会触发第一次,避免重复触发-->
        <button @click.once="btnClick">按钮</button>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
          },
          methods: {
            btnClick() {
              console.log('btnClick');
            }
          },
        });
      </script>
      
      </body>
      

    15、v-if、v-else-if、v-else

    Vue里面也提供了条件判断,这点是和绝大多数语言都是相通的,举个简单例子来看一下。

    <div id="app">
      <div v-if="score>=90">
        <h2>成绩优秀</h2>
      </div>
      <div v-else-if="score>=60">
        <h2>成绩及格</h2>
      </div>
      <div v-else>
        <h2>成绩不合格</h2>
      </div>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          score: 90
        },
      });
    </script>
    
    </body>
    

    注意为了提高代码可读性建议做到if、elseif、else同级并列。并且通常不要在html里写太多逻辑判断的东西i,最好是在计算属性中进行复杂的逻辑判断然后返回给View。举个例子:

    <body>
    
    <div id="app">
      <h2>{{message}}</h2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          score: 90
        },
        computed: {
          message() {
            //先初始化一个返回值
            let returnMessage='';
            if (this.score>=90){
              returnMessage='成绩优秀';
            }else if (this.score>=60){
              returnMessage='成绩及格';
            }else {
              returnMessage='成绩不合格';
            }
            //返回这个初始化的返回值
            return returnMessage;
          }
        },
      });
    </script>
    
    </body>
    

    直接在computed计算属性里面进行逻辑判断然后正确返回就好了。条件判断跟其他的编程语言基本没啥区别,主要就是需要注意的就是最好在view里面顶多进行简单的逻辑判断,复杂的逻辑判断交给计算属性。

    16、Vue的Dom会复用的问题

    在Vue的底层中其实是会先将Dom渲染成虚拟Dom最后再将虚拟Dom渲染到页面上的。有的时候Vue为了提高渲染时候的效率,Vue会自主地将它认为不可能产生冲突的两个Dom元素渲染成为一个虚拟Dom,这样的话就不会再额外创建另外一个虚拟Dom,可以大幅提升效率。

    举个简单例子来说:

    <body>
    
    <div id="app">
      <span v-if="isUserName">
        <label for="username">用户账号</label>
        <input type="text" id="username" placeholder="请在这里输入你的用户账号">
      </span>
      <span v-else>
        <label for="email">用户邮箱</label>
        <input type="email" id="email" placeholder="请在这里输入你的用户邮箱">
      </span>
      <button @click="changeLoginType">切换登录方式</button>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          isUserName: true
        },
        methods: {
          //切换登录方式
          changeLoginType() {
            this.isUserName=!this.isUserName;
          }
        },
      });
    </script>
    
    </body>
    

    这个需求就是通过二元来判断是显式用户账号输入框还是用户邮箱输入框,因为这两个span是互斥的,不可能同时存在,所以Vue就会自主地将他们两个Dom渲染成为一个虚拟Dom,当切换到另外一个输入框的时候就不需要再重新创建一个新的Dom。所以,其实两个span里的input输入框是被复用了的,所以当你在一个输入框里输入value的时候然后切换到另外一个输入框,你刚才输入的value仍然还在,这种情况有时候是不想出现的,我们更多的是想切换登录方式后再清除掉原有的输入内容。那么,我们就可以给这两个input输入框加上唯一的标识key,这样Vue就不会再擅作主张的把两个渲染成一个虚拟Dom了,而是不同的虚拟Dom。

      <span v-if="isUserName">
        <label for="username">用户账号</label>
        <input type="text" id="username" placeholder="请在这里输入你的用户账号" key="username">
      </span>
      <span v-else>
        <label for="email">用户邮箱</label>
        <input type="text" id="email" placeholder="请在这里输入你的用户邮箱" key="email">
      </span>
    

    这个需要视使用场景而定,假如你切换了输入框后想要保留原有内容你就不会唯一标识key,使得Vue渲染成为同一个虚拟Dom,这样效率也会高。如果你需要切换输入框后不保留原有内容甚至情况,那么就加上唯一标识key,使得Vue渲染成为不同的虚拟Dom。

    17、v-show与v-if的区别

    v-if操作的是html的Dom元素,当if里面的boolean值为false时,那么v-if所在的整个dom元素都不存在,而v-show里面的boolean值如果是false的话,那么Vue其实仅仅是个这个元素加上了style="display: none;"的样式隐藏了而已。

    18、v-for遍历对象里面的元素

    v-for不仅仅可以遍历数组、集合,还可以遍历对象中的属性,比如这个student对象:

    	  //student对象
          student:{
            id:10001,
            name:'张三',
            age:18,
          }
    

    我想要依次把这个对象的属性遍历出来我也可以使用v-for来遍历出来,像这样:

    <body>
    
    <div id="app">
      <!--遍历value-->
      <ul>
        <li v-for="item in student">{{item}}</li>
      </ul>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          //student对象
          student:{
            id:10001,
            name:'张三',
            age:18,
          }
        },
      });
    </script>
    
    </body>
    

    这样就可以遍历这个对象,但是只能遍历出属性的值,也就是value,假如我们想要遍历出属性名,也就是key怎么办呢?像这样:

      <!--遍历key-value-->
      <ul>
        <li v-for="(value,key) in student">{{key}}--{{value}}</li>
      </ul>
    

    **注意:**这里的value只能在一个参数位置,位置不能改变。

    同时也可以遍历出下标index,像这样:

      <!--遍历key-value和下标-->
      <ul>
        <li v-for="(value,key,index) in student">{{index}}--{{key}}--{{value}}</li>
      </ul>
    

    19、常用的数组响应式方法

    1. push():向尾部插入元素

              // 1.push:向尾部插入元素
              this.letters.push('e');//插入单个元素
              this.letters.push('e','x','a','m');//插入多个元素
      
    2. pop():从尾部移除元素

              // 2.pop:从尾部移除元素
              this.letters.pop();
      
    3. unshift():向首部插入元素

              // 3.unshift:向首部插入元素
              this.letters.unshift('e');//插入单个元素
              this.letters.unshift('e','x','a','m');//插入多个元素
      
    4. shift():从首部移除元素

              // 4.shift:从首部移除元素
              this.letters.shift();
      
    5. sort():元素自动排序(按一定规则)

              // 5.sort:数组排序
              this.letters.sort();
      
    6. reverse():元素反转

              // 6.reverse:数组反转
              this.letters.reverse();
      
    7. splice():元素删除/插入/替换

      splice方法有一个重载方法:

      splice(start: number, deleteCount?: number): T[];
      splice(start: number, deleteCount: number, ...items: T[]): T[];
      

      start:指定元素开始的下标

      deleteCount:指定要删除元素的个数

      items:插入或者替换的多个元素

      元素删除:

              //从数组下标为1的元素开始,删除2个元素
              this.letters.splice(1,2);
      

      元素插入:(当第二个参数为0的时候代表插入元素)

              //在数组下标为1的元素后面插入多个元素
              this.letters.splice(1,0,'e','x','a','m');
      

      元素替换:

              //在数组下标为1的元素后面的2个元素替换为多个元素
              this.letters.splice(1,2,'e','x','a','m');
      

      其实我们可以理解成这三种操作实际上都是在对数组中的元素进行删除操作。当第二个参数有数字的时候代表要删除多少个元素,替换的时候我们也可以理解成先删掉元素再进行追加元素。当第二个参数为0的时候就代表不删除元素,然后直接进行追加元素。

    **特别注意:**使用如下这种方法并不能响应式的改变数组元素:

    this.letters[0]='example';
    

    如果想要响应式的替换元素可以采用:

    this.letters.splice(0,1,'example');
    

    20、在Vue中使用filters过滤器

    过滤器同样是Vue的一个options参数,叫属性名叫filters,值是一个对象,像这样:

        filters: {
          showPrice(price) {
            //toFixed(2)保留小数点后两位
            return '¥'+price.toFixed(2);
          }
        }
    

    使用的时候就是像这样使用的:

          <td>{{book.price | showPrice}}</td>
    

    在这个参数后面加上|管道流符号,再加上过滤器的名字(注意,这个时候就不需要传递参数了,因为管道会自动将前面这个变量当作参数传递给后面的过滤器),像这样:

          <td>{{showPrice(book.price)}}</td>
    

    21、v-model框双向绑定输入框

    在表单中我们时常需要将model层里的数据映射到view层上,并且最好能够实现其中一个改变另外一个也跟着改变,v-model双向绑定就帮我们实现了这个需求。

    <body>
    
    <div id="app">
      <input type="text" v-model="message">
      <h2>{{message}}</h2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
        },
      });
    </script>
    
    </body>
    

    只要你改变data里的数据或者改变输入框中的值,model层和view里输入框中的值也会发生改变。

    22、v-model双向绑定radio

    <body>
    
    <div id="app">
      <label>性别:</label>
      <input type="radio" name="sex" value="" v-model="sex"><input type="radio" name="sex" value="" v-model="sex"><h3>你选择的性别是:{{sex}}</h3>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          sex:''
        },
      });
    </script>
    
    </body>
    

    直接在每个radio里用v-model绑定sex,如果在data里的sex没有赋初始值,那么input里的radio默认就什么都不选,如果初始值赋了其中一个,那么默认就会选择那一个。

    23、v-model双向绑定checkbox

    1. 单选框

      <body>
      
      
      <div id="app">
        <!--v-model双向绑定结合checkbox实现单选框-->
        <label for="license">
          <input type="checkbox" id="license" value="同意" v-model="isAgree">我已经完整阅读上述协议
        </label>
        <h2>您选择的是:{{isAgree | showAgree}}</h2>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            isAgree: false,
          },
          filters: {
            showAgree: function (value) {
              if (value===true){
                return '同意';
              }else if (value===false){
                return '不同意';
              }
            }
          }
        });
      </script>
      
      
      </body>
      

      同样,直接用v-model绑定data里面的isAgree就行了,绑定boolean值,true就是选中,false就是不被选中,为了方便观察读出的结果,我们可以使用过滤器将选中的boolean值格式化为同意不同意的字符串。

    2. 复选框

      <body>
      
      
      <div id="app">
      
        <!--v-model双向绑定结合checkbox实现复选-->
        <h2>爱好:</h2>
        <input type="checkbox" name="hobby" value="" v-model="hobbies"><input type="checkbox" name="hobby" value="" v-model="hobbies"><input type="checkbox" name="hobby" value="rap" v-model="hobbies">rap
        <input type="checkbox" name="hobby" value="篮球" v-model="hobbies">篮球
        <h2>你选择的爱好是:{{hobbies | showHobbies}}</h2>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            hobbies:[]
          },
          filters: {
            showHobbies(hobbies){
              let returnString='';
              for (let hobby of hobbies) {
                returnString+=hobby+' ';
              }
              return returnString;
            }
          }
        });
      </script>
      
      
      </body>
      

      依旧是直接使用v-model绑定data里的hobbies数组(因为复选框的结果是数组),同意为了方便展示,直接使用过滤器将hobbies数组格式化成字符串数组然后遍历显示出来。

    3. 与v-bind结合

      大多数时候我们应该是要从后端获得原始的checkbox要显示的数据,然后前端复选了后再传递到提交到后端。从后端获取到的数据肯定是数组,然后通过遍历数组然后进行初始显示到页面上供用户选择。所以最开始肯定使用label来遍历数组:

      <body>
      
      <div id="app">
        <label v-for="item in originHobbies">
          <input type="checkbox" :value="item" v-model="hobbies" >{{item}}
        </label>
        <h2>你选择的爱好是:{{hobbies}}</h2>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
        let app = new Vue({
          el: '#app',
          data: {
            //要显示在checkbox里的供用户选择的爱好数组(从后端获取)
            originHobbies:['唱','跳','rap','篮球'],
            //从前端得到然后再返回给后端
            hobbies: []
          },
        });
      </script>
      
      </body>
      

      因为遍历出来的数据肯定不止一个,所以要使用v-bind动态绑定value从而得到多个值,然后再通过v-model绑定到选择好的数组里供传递给后端。

    24、v-model双向绑定select

    <body>
    
    <div id="app">
    
      <!--单选-->
      <select name="fruit" v-model="fruit">
        <option value="香蕉">香蕉</option>
        <option value="苹果">苹果</option>
        <option value=""></option>
      </select>
      <h2>你选择的水果是:{{fruit}}</h2>
    
      <!--多选-->
      <select name="fruits" v-model="fruits" multiple size="3">
        <option value="香蕉">香蕉</option>
        <option value="苹果">苹果</option>
        <option value=""></option>
      </select>
      <h2>你选择的水果是:{{fruits | showFruits}}</h2>
    </div>
    
    <script src="../js/vue.js"></script>
    <script>
      let app = new Vue({
        el: '#app',
        data: {
          fruit:'香蕉',
          fruits:[]
        },
        filters: {
          showFruits: function (fruits) {
            let returnString='';
            for (let fruit of fruits) {
              returnString+=fruit+' ';
            }
            return returnString;
          }
        }
      });
    </script>
    
    </body>
    

    select标签里通过添加multiple属性指定变成多选,同样是需要在select里添加v-model绑定data里面的数据。

    25、v-model修饰符

    1. lazy修饰符

      因为input输入框的特性,v-model绑定了data中的数据的时候是输入的时候立马把值附到data的数据里面的,而通常有的时候我们并不需要即使得到值,而是希望用户在输入框里失去焦点的时候再响应事件,比如用户按enter或者在输入框外面点击的时候,就直接给v-model加上.lazy修饰符:

        <!--1、lazy修饰符(懒加载)-->
        <input type="text" v-model.lazy="message">
        <h3>你输入的内容是:{{message}}</h3>
      

      现在就是用户按enter或者在输入框外点击的时候才会触发事件(赋值),而不是随时都在赋值。

    2. number修饰符

      因为html的input标签的特性,你所有的文本框中输入的内容都是字符串类型,即使你给强制指定了type为number但是你传递给后端的时候依旧是字符串类型,所以大多数时候你还需要在后端做一次强制类型转换,这也是html的input标签的特性,Vue的改进,只需要给v-model添加.number修饰符就可以在input框里作强制类型转换而不需要用js来转换或者在后端进行强制类型转换。比如:

        <!--2、number修饰符(强制转换为数值类型)-->
        <input type="number" v-model.number="number">
        <h3>你输入的{{number}}是{{typeof number}}类型</h3>
      
    3. trim修饰符

      trim修饰符就比较简单,就是去除input字符串里前和后的空格。

        <!--3、trim修饰符(字符串前后去空格)-->
        <input type="text" v-model="message">
        <h3>你输入的内容是:{{message}}</h3>
      

    26、Vue的组件化开发

    Vue可以将代码块封装成为一个组件,相互独立,方便管理和维护。同时还可以提高代码的复用性。

    组件化开发的过程:

    1. 创建组件构造器(Vue.extend()

        //1、创建组件构造器(需要一个extendOptions对象作为参数)
        let cpnConstructor = Vue.extend({
          template:`
            <div>
              <h2>标题</h2>
              <h3>内容</h3>
            </div>
          `
        });
      

      注意template属性的值可以使用html代码,同时结合ES6新语法使用tab键上的`号就可以达到将多行的代码直接换行,而不是以前的单引号加回车。

    2. 注册组件(Vue.component()

        //2、注册组件(第一个参数是给这个自定义组件取的名字,第二个参数是组件构造器)
        Vue.component('cpn',cpnConstructor);
      

      因为html中不支持驼峰命名,所以自定义的组件名不可以使用驼峰命名法。

    3. 使用组件

      <div id="app">
        <!--使用cpn组件-->
        <cpn></cpn>
        <!--cpn组件复用-->
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
      </div>
      

      直接在需要使用的html标签里使用就行了,前提是这个标签要交给Vue托管。

    4. 全部代码

      <body>
      
      <div id="app">
        <!--使用cpn组件-->
        <cpn></cpn>
        <!--cpn组件复用-->
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
      
        //1、创建组件构造器(需要一个extendOptions对象作为参数)
        let cpnConstructor = Vue.extend({
          //template就是自定义组件的模板
          template:`
            <div>
              <h2>标题</h2>
              <h3>内容</h3>
            </div>
          `
        });
      
        //2、注册组件(第一个参数是给这个自定义组件取的名字,第二个参数是组件构造器)
        Vue.component('cpn',cpnConstructor);
      
        let app = new Vue({
          el: '#app',
          data: {
          },
        });
      </script>
      
      </body>
      

      注意,需要先创建组件构造器然后注册组件,然后再用Vue托管html里的组件。

    27、全局组件和局部组件

    1. 全局组件

      全局组件顾名思义就是所有的Vue实例对象里都可以使用的组件,刚刚上述注册的组件就是全局组件可以在另外一个Vue实例中使用,比如:

      <body>
      
      <div id="app">
        <cpn1></cpn1>
      </div>
      
      <div id="app2">
        <cpn1></cpn1>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
      
        //组件构造器
        let cpnConstructor1 = Vue.extend({
          template:`
          <div>
            <h2>全局组件</h2>
            <h3>全局组件</h3>
          </div>
          `
        });
      
        //注册全局组件cpn1
        Vue.component('cpn1',cpnConstructor1);
      
        let app = new Vue({
          el: '#app',
        });
      
        let app2 = new Vue({
          el: '#app2',
        });
      </script>
      
      </body>
      

      使用Vue.component()注册的组件就是全局组件,可以供所有的Vue实例对象使用,比如上述梨子就供#app1和#app2使用了

    2. 局部组件

      局部组件就是只能在哪个Vue实例中注册的,就只能在该Vue实例中使用。比如:

      <body>
      
      <div id="app">
        <cpn2></cpn2>
      </div>
      
      <div id="app2">
        <cpn2></cpn2>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
      
        //组件构造器
        let cpnConstructor2 = Vue.extend({
          template:`
          <div>
            <h2>局部组件</h2>
            <h3>局部组件</h3>
          </div>
          `
        });
      
        let app = new Vue({
          el: '#app',
          components:{
            cpn2:cpnConstructor2
          }
        });
      
        let app2 = new Vue({
          el: '#app2',
        });
      </script>
      
      </body>
      

      因为cpn2组件只在#app里注册了,所有就只能在app这个Vue实例中使用,而不能在app2这个Vue实例中使用。同时局部组件的注册方法是利用传递Vue实例的Options属性的方法:

        let app = new Vue({
          el: '#app',
          components:{
            //自定义组件的名字:组件的构造器
            cpn2:cpnConstructor2
          }
        });
      

    28、父组件和子组件

    如果想在组件里使用另外的组件的话,就需要用到组件的嵌套。步骤如下:

    1. 注册多个组件

        //创建组件构造器2
        let cpnConstructor2 = Vue.extend({
          template:`
          <div>
            <h2>我是组件2的标题</h2>
            <h3>我是组件2的内容</h3>
          </div>
          `
        });
      
        //创建组件构造器1
        let cpnConstructor1 = Vue.extend({
          template:`
          <div>
            <h2>我是组件1的标题</h2>
            <h3>我是组件1的内容</h3>
          </div>
          `
        });
      
    2. 在Vue实例里注册局部组件

        let app = new Vue({
          el: '#app',
          data: {
          },
          components:{
            //在app里注册局部组件cpn1
            cpn1:cpnConstructor1
          }
        });
      
    3. 在cpn1组件的构造器里的注册cpn2组件并在template中使用

        let cpnConstructor1 = Vue.extend({
          template:`
          <div>
            <h2>我是组件1的标题</h2>
            <h3>我是组件1的内容</h3>
            <cpn2></cpn2>
          </div>
          `,
          components:{
            //在cpn1里注册cpn2
            cpn2:cpnConstructor2
          }
        });
      

      注:在cpn1里注册cpn2,那么久只能在cpn1的template中使用cpn2

      此时,cpn1就是父组件,cpn2就是子组件

    4. 所有代码

      <body>
      
      <div id="app">
        <cpn1></cpn1>
      </div>
      
      <script src="../js/vue.js"></script>
      <script>
      
        //创建组件构造器2(子组件)
        let cpnConstructor2 = Vue.extend({
          template:`
          <div>
            <h2>我是组件2的标题</h2>
            <h3>我是组件2的内容</h3>
          </div>
          `
        });
      
        //创建组件构造器1(父组件)
        let cpnConstructor1 = Vue.extend({
          template:`
          <div>
            <h2>我是组件1的标题</h2>
            <h3>我是组件1的内容</h3>
            <cpn2></cpn2>
          </div>
          `,
          components:{
            //在cpn1里注册cpn2
            cpn2:cpnConstructor2
          }
        });
      
        let app = new Vue({
          el: '#app',
          data: {
          },
          components:{
            //在app里注册局部组件cpn1
            cpn1:cpnConstructor1
          }
        });
      </script>
      
      </body>
      

    29、组件模板分离写法

    在上述代码中,我们创建组件构造器的时候需要给template属性传递参数,参数是html代码,这样就会导致js代码里面夹杂了大量html代码,看起来会非常杂糅,所以为了改变这一现象并且使得代码看起来不那么杂糅,推荐使用模板分离写法。把html代码写在body标签里,给一个唯一的id给这个模板,然后将它赋给template属性。

    1. html模板

      <template id="cpn">
        <div>
          <h2>我是标题</h2>
          <h3>我是内容</h3>
        </div>
      </template>
      

      给这个模板赋id

    2. 作为参数将模板传递给template

        //创建组件构造器
        let cpnConstructor = Vue.extend({
          template:'#cpn'
        });
      

      将这个模板传递给template

    3. 完整代码

      <body>
      
      <div id="app">
        <cpn></cpn>
      </div>
      
      <template id="cpn">
        <div>
          <h2>我是标题</h2>
          <h3>我是内容</h3>
        </div>
      </template>
      
      <script src="../js/vue.js"></script>
      <script>
      
        //创建组件构造器
        let cpnConstructor = Vue.extend({
          template:'#cpn'
        });
      
        //注册组件为全局组件
        Vue.component('cpn',cpnConstructor);
      
        let app = new Vue({
          el: '#app',
        });
      </script>
      
      </body>
      

    30、组件里的data

    组件里不能使用Vue实例里的data,所以组件里必须要有自己的data,就像“我的附庸的附庸不是我的附庸”一样。然而在组件里声明数据的方式跟在Vue实例里声明数据的方式有所不用,组件中是用函数来声明的,然后返回值返回的是数据,像这样:

      //创建组件构造器
      let cpnConstructor = Vue.extend({
        template: '#cpn',
        data(){
          return {
            message: 'Hello,Vue.js!'
          }
        }
      });
    

    这里比较特殊的就是利用函数来声明data,然后返回具体的数据的目的就是为了保证利用同一个组件构造器构造的多个组件里的数据不共用,每个组件各自有各自独立的数据。然后在template中使用:

    <template id="cpn">
      <div>
        <h2>我是标题</h2>
        <h3>{{message}}</h3>
      </div>
    </template>
    

    31、父子组件之间通信(父传子利用props)

    在绝大多数真实开发过程中,父子组件是不可能完全隔离的,他们之间也会互相传输数据,比如子组件会从父组件获取数据,父组件也会给子组件传递参数,这之间的行为就好像“通信”一样。而父组件向子组件通信的方式是利用props。基本步骤如下:

      //子组件
      let cpnConstructor = Vue.extend({
        template:'#cpn',
        //子组件从父组件获取属性
        props: {
    
          //子属性cmessage
          cmessage: {
            //指定从父组件获取的属性的类型
            type: String,
            //要求父组件必须向子组件传递该参数
            required:true
          },
    
          //子属性cmovies
          cmovies: {
            type: Array,
            //设置假如父组件没有向子组件传递该参数的默认值
            default(){//类型是数组或者对象的时候,默认值必须是函数
              return ['movie1','movie2','movie3'];
            }
          }
    
        },
      });
    

    在子组件里定义props属性,对象参数里定义将父组件获取到的参数的自定义属性名,参数同样是对象,该对象的参数有type(指定传入参数的数据类型),required(指定是否必须传入参数,true为必须),default(指定传入参数的默认值)。其中default比较特殊,如果传入的参数是数组或者对象类型,那么default必须指定为函数,然后将默认值返回。

    <!--root组件-->
    <div id="app">
      <!--在root组件里使用cpn组件-->
      <cpn v-bind:cmessage="message" v-bind:cmovies="movies"></cpn>
    </div>
    

    同时在需要用到参数的父组件里声明子组件,并且在该子组件标签里利用v-bind将父组件里的参数绑定到子组件里。比如:

    v-bind:cmessage="message"
    <!-- cmessage是在子组件的props里声明的参数,message是在父组件的data中的数据 -->
    

    然后再子组件里就可以使用了:

    <!--cpn组件模板-->
    <template id="cpn">
      <div>
        <h2>{{cmessage}}</h2>
        <ul>
          <li v-for="movie in cmovies">{{movie}}</li>
        </ul>
      </div>
    </template>
    

    cmessage和cmovies就是子组件里props里的属性,就可以直接使用了。

    完整代码:

    <body>
    
    <!--root组件-->
    <div id="app">
      <!--在root组件里使用cpn组件-->
      <cpn v-bind:cmessage="message" v-bind:cmovies="movies"></cpn>
    </div>
    
    <!--cpn组件模板-->
    <template id="cpn">
      <div>
        <h2>{{cmessage}}</h2>
        <ul>
          <li v-for="movie in cmovies">{{movie}}</li>
        </ul>
      </div>
    </template>
    
    <script src="../js/vue.js"></script>
    <script>
    
      //子组件
      let cpnConstructor = Vue.extend({
        template:'#cpn',
        //子组件从父组件获取属性
        props: {
    
          //子属性cMessage
          cmessage: {
            //指定从父组件获取的属性的类型
            type: String,
            //要求父组件必须向子组件传递该参数
            required:true
          },
    
          //子属性cMovies
          cmovies: {
            type: Array,
            //设置假如父组件没有向子组件传递该参数的默认值
            default(){//类型是数组或者对象的时候,默认值必须是函数
              return ['movie1','movie2','movie3'];
            }
          }
    
        },
      });
    
      //root组件(根组件)
      let app = new Vue({
        el: '#app',
        data: {
          message: 'Hello,Vue.js!',
          movies:['钢铁侠1','钢铁侠2','钢铁侠3']
        },
        //在root组件里注册子组件
        components:{
          cpn:cpnConstructor
        }
      });
    </script>
    
    </body>
    

    **Tips:**因为html里英文字母大小写是不敏感的,所以不支持驼峰命名的,但是vue里面支持,所以在html标签里使用到驼峰的地方就添加横杠-,比如cMessage就应该变成c-message。或者索性直接不使用驼峰命名。在父组件的使用子组件的标签上加上v-bind:变量名1="变量名2"来向子组件传递参数,子组件通过props和变量名1来获得参数。

    32、父子组件之间通信(子传父利用$emit)

    在开发的过程中,有的时候也需要通过子组件来向父组件传递数据或者对象的时候,比如分类列表,大多数时候分类列表的二级列表里的东西应该是根据一级分类列表的数据来变化的,所以这个时候其实是根据一级菜单点击事件然后向服务器发送请求,获得二级列表里的数据然后来进行显示的。所以,子传父其实是利用子组件里产生事件,然后发送事件,父组件再接收(监听)事件,然后进行相应的处理。

    模拟子组件和父组件,父组件的data里包含一级列表数组对象,然后将子组件注册到父组件里:

      //子组件
      let cpnConstructor = Vue.extend({
        template:'#cpn',
        data(){
          return {
            categories:[
              {id:1,name:'英语'},
              {id:2,name:'数学'},
              {id:3,name:'语文'},
            ]
          }
        },
      });
    
      //root组件(父组件)
      let app = new Vue({
        el: '#app',
        data: {
    
        },
        components:{
          //将子组件注册到父组件里
          cpn:cpnConstructor
        },
      });
    

    子组件的模板template,以按钮的形式遍历出分类按钮:

    <!--子组件模板-->
    <template id="cpn">
      <div>
        <!--循环遍历出分类-->
        <button v-for="item in categories">{{item.name}}</button>
      </div>
    </template>
    

    此时需要给按钮添加点击事件,在该事件里利用this.$emit()发射该事件,并且传递对象:

      //子组件
      let cpnConstructor = Vue.extend({
        template:'#cpn',
        data(){
          return {
            categories:[
              {id:1,name:'英语'},
              {id:2,name:'数学'},
              {id:3,name:'语文'},
            ]
          }
        },
        methods: {
          btnClick(item) {
            console.log('子组件---->',item);
            //将子组件的事件发送出去,并且自定义事件名为item-click
            this.$emit('item-click',item);
          }
        },
      });
    

    'item-click'是自定义的事件名,表明在外部需要监听(接收)该事件,item是根据这个事件一起传递的对象,外部接收到该事件后同时也会接收到该对象。

    在父组件里通过监听刚刚自定义的事件(item-click):

    <!--root(父)组件模板-->
    <div id="app">
      <!--在父组件里中使用子组件-->
      <cpn v-on:item-click="itemClick"></cpn>
    </div>
    

    然后利用监听事件获得对象:

      //root组件(父组件)
      let app = new Vue({
        el: '#app',
        data: {
    
        },
        components:{
          //将子组件注册到父组件里
          cpn:cpnConstructor
        },
        methods: {
          itemClick(item) {
            console.log('父组件---->',item);
          }
        },
    

    33、父传子、子传父之间的区别

    父组件传子组件是利用props传递:在父组件里利用v-bind将父组件里的数据主动绑定到子组件上。父传子是子被动集成父组件数据。

    子组件传父组件是利用$emit传递:在子组件里利用​this.$emit主动发射事件,然后父组件里监听该事件。子传父是子主动发射事件。

    34、父组件访问子组件中数据

    开发的过程中假如我们需要在父组件中直接使用子组件的东西那么我们就需要直接访问到子组件内部,Vue提供了两种方法供父组件访问子组件,$children$refs

    1. 父组件利用$children访问子组件

      <body>
      
      <!--父组件模板-->
      <div id="app">
        <cpn></cpn>
        <cpn></cpn>
        <cpn></cpn>
        <button @click="btnClick">按钮</button>
      </div>
      
      <!--子组件模板-->
      <template id="cpn">
        <div>
          <h3>我是子组件</h3>
        </div>
      </template>
      <script src="../js/vue.js"></script>
      <script>
      
        // 子组件
        let cpnConstructor = Vue.extend({
          template: '#cpn',
          data() {
            return {
              message: '我是子组件的message',
            }
          },
          methods: {
            showMessage() {
              console.log('我是子组件的showMessage()');
            }
          },
        });
      
        // 父组件
        let app = new Vue({
          el: '#app',
          data: {
          },
          components:{
            //在父组件中注册子组件
            cpn: cpnConstructor
          },
          methods: {
            btnClick() {
              console.log(this.$children);// 子组件对象数组
              console.log(this.$children[0].message);// 第一个子组件对象的message属性
              this.$children[0].showMessage();// 调用第一个子组件对象的showMessage()方法
            }
          },
        });
      </script>
      
      </body>
      

      我们可以在父组件里使用this.$children拿到在这个父组件里使用的子组件对象数组,然后直接通过调用第几个子组件对象的属性或者方法就可以直接访问子组件。只需要记住this.$children拿到的是子组件对象数组就好了。

    2. 父组件里用$refs访问子组件

      <body>
      
      <!--父组件模板-->
      <div id="app">
        <cpn ref="cpn1"></cpn>
        <cpn ref="cpn2"></cpn>
        <cpn ></cpn>
        <button @click="btnClick">按钮</button>
      </div>
      
      <!--子组件模板-->
      <template id="cpn">
        <div>
          <h3>我是子组件</h3>
        </div>
      </template>
      <script src="../js/vue.js"></script>
      <script>
      
        // 子组件
        let cpnConstructor = Vue.extend({
          template: '#cpn',
          data() {
            return {
              message: '我是子组件的message',
            }
          },
          methods: {
            showMessage() {
              console.log('我是子组件的showMessage()');
            }
          },
        });
      
        // 父组件
        let app = new Vue({
          el: '#app',
          data: {
          },
          components:{
            //在父组件中注册子组件
            cpn: cpnConstructor
          },
          methods: {
            btnClick() {
              console.log(this.$refs.cpn1); // 拿到ref为cpn1的子组件对象
              console.log(this.$refs.cpn1.message); // 拿到子组件对象里的message属性
              this.$refs.cpn1.showMessage(); // 调用子组件对象里的showMessage()方法
            }
          },
        });
      </script>
      
      </body>
      

      如果在使用子组件的地方指定ref属性(相当于给这个组件设置一个id,下文就需要根据这个id来取),在父组件里使用this.$refs.+ref名,比如this.$refs.cpn1就是取上文ref为cpn1的子组件,然后再通过.message或者.showMessage()调用该组件里的属性或者方法。

    这两种方法可以混合使用,但是开发中建议使用第二种,毕竟直接指定名字取比循环遍历取方法快捷并且易于维护。

    35、子组件访问父组件中数据

    同父组件访问子组件类似,子组件访问父组件也是利用this.$parentthis.$root访问。两者的区别在于this.$parent访问的是父组件,而this.$root访问的是根组件(顶层的Vue实例)。

    36、slot插槽的基本使用

    在组件化开发中,我们可能会尽量地避免重复地去写某一个组件,有的时候有的组件可以复用的话尽量还是会选择复用。但是复用的话其实这两个需要用到这个组件的地方肯定还是会有不同的地方,这个时候我们就要用到插槽。插槽就是给同一个组件里动态填充不同的小部件、小标签。比如:

    <template id="cpn">
      <div>
        <h2>我是子组件</h2>
        <!--在组件中预留插槽-->
        <slot></slot>
      </div>
    </template>
    

    我定义了一个cpn的模板,里面有一个<slot>标签,这个就是给这个组件预留的插槽,外部在使用的时候就直接动态放入想要显示的东西就行了。其余部分就是组件共有的。

    直接在需要的地方使用就可以了,注意需要包围到该组件标签的中间:就是包围到<cpn></cpn>的中间。

    <div id="app">
      <!--在cpn内部使用标签就会自动填充到slot插槽里了-->
      <cpn><button>我是插槽1的按钮</button></cpn>
      <cpn><span>我是插槽2的span标签</span></cpn>
      <cpn><p>我是插槽3的p标签</p></cpn>
    </div>
    

    相应的,这三个标签就会动态地插入到slot的插槽里面了。同时还可以给slot插槽指定默认标签(如果外部没有指定标签,就会使用默认标签),同样也是包围在<slot></slot>标签中间。比如:

    <template id="cpn">
      <div>
        <h2>我是子组件</h2>
        <!--在组件中预留插槽-->
        <slot><p>我是子组件slot插槽里的默认标签</p></slot>
      </div>
    </template>
    

    在使用的时候就会使用slot标签中默认的标签进行填充。

    <div id="app">
      <!--在cpn内部使用标签就会自动填充到slot插槽里了-->
      <cpn><button>我是插槽1的按钮</button></cpn>
      <cpn><span>我是插槽2的span标签</span></cpn>
      <cpn><p>我是插槽3的p标签</p></cpn>
      <!--没有给cpn指定标签,那么就会使用默认标签-->
      <cpn></cpn>
    </div>
    

    37、slot具名插槽

    假如说一个组件里有多个插槽,从外部想要给指定插槽添加东西的话就需要用到具名插槽。让一个slot变成具名插槽只需要给slot加上name属性就可以了,比如:

    <template id="cpn">
      <div>
        <h3>我是子组件</h3>
        <slot name="left"></slot>
        <slot name="center"></slot>
        <slot name="right"></slot>
        <slot><p>我是没有具名的插槽</p></slot>
      </div>
    </template>
    

    在外部使用的时候加上slot属性,就可以给指定的插槽添加标签了,如果没有指定slot属性,那么在添加标签的时候也会向没有具名的插槽添加,比如:

    <div id="app">
      <cpn>
        <!--p标签包括标签里的内容会替换没有具名的插槽-->
        <p>我会替换没有具名的插槽</p>
        <!--button标签包括标签里的内容会替换名为left的插槽-->
        <button slot="left">我会替换left插槽</button>
        <i slot="center">我会替换center插槽</i>
        <p slot="right">我会替换right插槽</p>
      </cpn>
    </div>
    

    38、slot作用域插槽

    有些时候我们会在组件的插槽里写好样式的标签,可是有的时候我们有需要在组件的外部自定义组件的样式的标签,但是数据仍然使用组件本身的数据,就需要使用作用域插槽。

    <!--子组件模板-->
    <template id="cpn">
      <div>
        <!--将movies绑定到data(自定义变量名)上,暴露给外部(外部通过data来获取)-->
        <slot :data="movies">
          <!--slot插槽里默认是ul>li循环遍历-->
          <ul>
            <li v-for="(item,index) in movies">{{index}}-{{item}}</li>
          </ul>
        </slot>
      </div>
    </template>
    

    在slot插槽里指定:data="movies"属性(data是自定义的,方便外部获取),movies就是这个组件里的数据。

    <div id="app">
      <!--使用默认的组件-->
      <cpn></cpn>
      <!--使用组件并且向组件插槽里自定义标签-->
      <cpn>
        <!--获得组件的slot插槽-->
        <template slot-scope="slot">
          <!--循环遍历slot插槽里的data数组-->
          <span v-for="item in slot.data">{{item}} - </span>
        </template>
      </cpn>
    </div>
    

    在用到该组件的<cpn></cpn>标签里利用template标签,然后slot-scope属性绑定slot插槽,然后利用slot.data获得刚刚在组件里向外部暴露的数据,然后再进行遍历。

    **总结:**作用域插槽就是能够在组件的外部自定义样式标签,然后外部使用组件里的插槽里的数据。

    外部使用template标签里的slot-scope获得slot插槽。

    内部使用:data来绑定组件里的数据。

    39、JavaScript模块化开发思想

    因为现在流行分工合作开发,所以难免会多人合作,所以多人合作的时候每个人都会有自己的模块,就会创建自己的js文件,这样提高了代码的可维护性和易读性,分工合作开发是主流也是趋势。但是这样时候就会陷入一个问题,就是因为js的特性,所有的js文件的作用域是在同一个,甚至在ES5以前,还没有let局部变量,所有的变量都是var全局变量,所以多人开发就极易导致变量或者函数重名,给程序带来灾难性毁灭。

    所以就引入了模块化开发。所谓模块化开发思想就是多人合作小组成员各自专注自己的代码,降低代码之间的耦合,提高开发效率。

    40、ES6模块化开发

    ES6规范是直接支持模块化开发的,而不用像CommonJS一样还需要依赖。模块化开发最需要解决的问题就是单个模块里自己需要暴露给其他模块和在自己模块引入其他模块。所以需要解决的核心问题就是导出(export)和引入(import)。

    1. 在html里直接用script标签引入,type类型必须指定为module(模块)

      <body>
      <!--模块化引入module1.js文件-->
      <script src="module1.js" type="module"></script>
      <!--模块化引入module2.js文件-->
      <script src="module2.js" type="module"></script>
      </body>
      
    2. 在module1.js里导出

      //定义变量
      let name='模块1';
      let age=18;
      
      //定义函数
      function sum(num1,num2) {
        return num1+num2;
      }
      
      //定义类
      class User {
        constructor() {}
      }
      
      //导出(暴露)
      export {
        name,age,sum,User
      }
      

      export { 变量名/函数名/类名 }导出

    3. 在module2.js里导入

      "use strict";
      //从module1.js文件里导入name,age,sum,obj
      import {name,age,sum,User} from "./module1.js";
      
      console.log(name);
      console.log(age);
      console.log(sum(20,30));
      console.log(User);
      

    就感觉上相当于模块中利用export把变量、函数、类导入到一个池中,然后再在需要的模块里利用import来导入。

    在这里插入图片描述

    41、ES6模块化开发导出和导入的几种方式

    除了上述的直接利用

    export{
      变量,函数,}
    

    的方式导出,还有下面的方式可以进行导出:

    export let name='模块1';
    export let age=18;
    export function sum(num1,num2) {
      return num1+num2;
    }
    export let obj={
      constructor(){}
    }
    

    直接在定义的时候就在前面加上export关键字就能导出了。还可以利用

    let name='模块1';
    export default name;
    

    利用export default导出的话表示该模块只能导出一个,然后在需要导入的模块就直接利用

    import test from "./module1.js";
    
    console.log(test);
    

    导入就可以了,因为导出的时候只能导出一个,所以导入的时候也就可以随便自定义名字了。

    最后一中导入方式,就是利用:

    import * as module1 from "./module1.js";
    
    console.log(module1.name);
    console.log(module1.age);
    console.log(module1.sum(20,30));
    console.log(module1.User);
    

    从module1.js文件里导入所有东西,然后取module1为别名,然后再利用module1.name或者.age获得模块里的内容。

    42、利用webpack配置vue

    关于webpack的介绍和使用可以参照这篇文章: https://blog.csdn.net/weixin_45747080/article/details/109166069

    webpack和Vue进行结合的话仍然需要以下几步:

    1. 利用npm命令安装vue

      npm install vue@2.5.21 --save
      

      因为我们在项目开发和项目上线的环境中都需要用到vue,所以这里就不用指定-dev

    2. 写vue的代码

      依旧是在index.html写

      <!--vue实例-->
      <div id="app">
        <h2>{{message}}</h2>
      </div>
      

      在js里先引入vue然后在创建vue实例

      import Vue from 'vue'
      
      let app = new Vue({
        el:'#app',
        data:{
          message:'Hello,webpack and vue!',
        }
      });
      

      我们利用npm安装好vue后就可以直接利用import从项目根目录导入vue了

    3. 在webpack.config.js里配置

      
      //从node的系统模块中获得path
      const path=require('path');
      
      //指定webpack打包的起点(来源)和终点(目的地)
      module.exports={
        //起点字符串路径
        entry:'./src/main.js',
        //终点对象
        output:{
          //路径(拼接字符串)
          path:path.resolve(__dirname,'dist'),
          //生成的文件名
          filename:'bundle.js',
        },
        resolve:{
          alias:{
            // 将vue映射到runtime-compiler(vue.esm.js里包含能够编译template的compiler)
            'vue$':'vue/dist/vue.esm.js'
          }
        }
      };
      

      依旧是需要配置导出导入的起点和终点。但是同时需要注意的是需要配置:

        resolve:{
          alias:{
            // 将vue映射到runtime-compiler(vue.esm.js里包含能够编译template的compiler)
            'vue$':'vue/dist/vue.esm.js'
          }
        }
      

      如果没有配置这个,那么vue只能在运行环境中使用,在编译的过程中就无法编译template,所以必须在webpack的配置文件里配置这一项。

    4. 利用webpack将整个项目打包

      npm run build
      

    43、Vue CLI脚手架的介绍

    CLI(Commend-Line Interface):命令行界面。

    Vue CLI是Vue官方推出的命令行界面的项目构建工具,俗称脚手架。脚手架简化了webpack的配置,变得更方便。

    44、Vue CLI的安装

    1. 脚手架的安装依旧依赖webpack:

      npm install -g @vue/cli
      

      全局安装vue,因为vue2.x的版本的命令不同于现在,所以@vue/cli实际上是安装的vue3.x及其以上的版本。

    2. 拉取vue2.x的模板

      虽然我们用的脚手架是3.0以上,但是我们如果需要利用2.x的方式初始化项目是不可以的,除非拉取2.x的模板:

      npm install -g @vue/cli-init
      

      依旧是全局安装@vue/cli-init

    3. 创建vue项目

      你既可以利用vue2.x创建项目也可以利用vue3.x创建项目

      • vue2.x命令

        vue init webpack 项目名称
        
      • vue3.x命令

        vue create 项目名称
        

    45、利用vue CLI创建vue项目

    直接使用vue2.x的命令创建项目:

    vue init webpack vue-test
    

    就可以创建一个名为vue-test的vue项目了

    在这里插入图片描述

    打开我们刚刚新建好的项目我们可以看到vue2.x的目录结构如下:

    在这里插入图片描述

    46、vue cli构建方式runtime-compiler和runtime-only的区别

    从main.js的代码来看,这两种构建方式差距并不大:

    runtime-compiler的main.js:

    import Vue from 'vue'
    import App from './App'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      components: {App},
      template: '<App/>'					//template
    })
    

    runtime-only的main.js:

    import Vue from 'vue'
    import App from './App'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      render: h => h(App)					//render函数
    })
    

    因为vue渲染页面的时候会将template解析成ast(抽象语言树)然后再利用render函数解析成virtuldom(虚拟dom)然后再渲染成UI。所以runtime-only就直接跳过了前两步,直接利用render函数解析成虚拟dom然后渲染到页面。所以runtime-only的效率会比runtime-compiler效率高,并且由于减少了解析template的代码,所以runtime-only构建的项目也会更小。所以真实开发中更倾向于用runtime-only

    47、利用vue-cli3创建项目

    之前我们利用vue-cli2理解了vue创建项目的原理以及众多配置文件,然而vue-cli3减少了很多配置文件,方便了许多。这里我们利用vue-cli3创建vue项目:

    vue create 项目名称
    

    然后就可以看到依旧是在创建项目前需要选择的选项:

    在这里插入图片描述

    利用vue-cli3创建完项目后可以发现与之前的项目结构目录有所不同:

    在这里插入图片描述

    利用vue-cli3创建的项目比vue-cli2简化了许多。并且因为vue-cli3支持GUI,所以我们可以直接在全局终端输入:

    vue ui
    

    从而直接打开vue-cli3自带的可视化项目管理界面:

    在这里插入图片描述

    你可以直接在网页里管理项目的插件和依赖以及项目的相关配置,甚至还可以直接启动项目。

    48、在Vue中的前端路由思想

    所谓后端路由就是用户在浏览器地址请求url然后后端的controller就会跳转到对应的这个url请求的页面。

    前端路由则是用户在浏览器地址请求url的时候服务器就会一次性把所有的静态资源都给请求下来了(html+css+js),然后在前端里通过js里写一些判断逻辑然后将对应的html页面加载出来。甚至单页面富应用的话就只请求一个html页面,但是后续切换页面的时候则会直接利用js生成新的html代码从而加载到页面上。

    49、在前端不刷新页面从而改变浏览器url

    在js中前端想要请求到新的页面需要使用windows.href='url'方法会跳转到对应的页面并且浏览器的url页面相应改变,但是这样每请求一个页面就会刷新一次页面,每重新刷新一次页面就会重新向后端发起一次请求。这样不太符合前端路由的思想,于是有了windows.hash='url',这样可以直接修改页面的url但是不会重新刷新页面。

    同时还有一种方法history.pushState(data,title,url)是类似栈的结构,可以改变页面的url,但是不会刷新页面,同时遵循先进后出的规则。使用浏览器的返回按键或者history.back()或者history.go(-1)就可以返回上一页,实际上也就是出栈。同时也可以使用浏览器的前进按钮或者history.forward()或者history.go(1)前进的前一页。

    50、vue-router的安装和配置

    在vue中,vue-router就是用来负责前端路由的映射的官方组件。

    安装vue-router可以在利用vue-cli创建项目的时候选择要安装vue-router或者在项目里直接使用:

    npm install vue-router --save
    

    此时你就需要在项目的src文件夹里面创建一个router文件夹,再新建一个index.js文件来配置路由映射的相关东西:

    // 1、引入vue-router和vue
    import VueRouter from 'vue-router';
    import Vue from 'vue';
    
    // 2、用vue.use()全局使用vue-router插件
    Vue.use(VueRouter);
    
    // 3、创建vue-router对象(和创建Vue实例类似)
    const router = new VueRouter({
      //配置路由和页面的映射关系
      routes:[
    
      ]
    });
    
    // 4、导出(暴露)给外部,使得Vue能够使用它
    export default router;
    
    

    因为我们肯定需要用到vue和vue-router,所以我们先引入他们,然后再用Vue.use()全局使用vue-router插件,其他插件也需要使用Vue.use()来启用。然后再创建router实例对象,里面得routes里配置路由和页面的映射关系。最后再export导出router对象以使得Vue能够接收。

    main.js:

    import Vue from 'vue'
    import App from './App'
    // 从router/index.js里导入路由组件
    import Router from './router/index'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router: Router,
      render: h => h(App)
    })
    
    

    51、利用vue-router切换页面

    1. 首先我们先创建两个页面(也就是vue的组件)

      Home.vue:

      <template>
        <div>
          <h2>{{title}}</h2>
          <p>{{content}}</p>
        </div>
      </template>
      
      <script>
        export default {
          name: "Home",
          data() {
            return {
              name: '主页',
            }
          },
          computed: {
            title() {
              return '我是'+this.name+'的标题';
            },
            content() {
              return '我是'+this.name+'的内容';
            }
          },
        }
      </script>
      
      <style scoped>
      
      </style>
      
      

      About.vue:

      <template>
        <div>
          <h2>{{title}}</h2>
          <p>{{content}}</p>
        </div>
      </template>
      
      <script>
        export default {
          name: "About",
          data() {
            return {
              name: '关于',
            }
          },
          computed: {
            title() {
              return '我是'+this.name+'的标题';
            },
            content() {
              return '我是'+this.name+'的内容';
            }
          },
        }
      </script>
      
      <style scoped>
      
      </style>
      
      
    2. 将这个两个组件在router里面引入并且配置映射关系

      
      // 1、引入vue-router和vue
      import VueRouter from 'vue-router';
      import Vue from 'vue';
      
      // 将两个页面导入
      import Home from "../components/Home";
      import About from "../components/About";
      
      // 2、用vue.use()全局使用vue-router插件
      Vue.use(VueRouter);
      
      // 3、创建vue-router对象(和创建Vue实例类似)
      const router = new VueRouter({
        //配置路由和页面的映射关系
        routes:[
          {
            // 输入/home时就会跳转到Home页面
            path: '/home',
            component: Home
          },
          {
            // 输入/about时就会跳转到About页面
            path: '/about',
            component: About
          },
        ]
      });
      
      // 4、导出(暴露)给外部,使得Vue能够使用它
      export default router;
      
      

      需要利用import将需要用到的页面先提前导入,然后再在routes数组里配置path和component的映射关系。

      **Tips:**需要导出的对象叫router路由对象,需要配置映射关系的是route,只不过是它的复数routes

    3. 在需要显示的页面里利用router标签显示

      最后在App.vue里利用router标签显示

      App.vue:

      <template>
        <div id="app">
          <!--去首页的超链接-->
          <router-link to="/home">首页</router-link>
          <!--去关于的超链接-->
          <router-link to="/about">关于</router-link>
          <!--显示内容-->
          <router-view/>
        </div>
      </template>
      

      router-link标签就是供点击的超链接,其中的to属性就是跳转到某个页面,它会被渲染成一个a标签

      router-view就是表示这个组件将在这里显示,有点类似html的iframe

      在这里插入图片描述

    52、利用路由修改网站主页

    我们观察到即使加了vue-router进入页面仍然需要点击,包括进入主页也是,但是正常的逻辑应该是进入主页后自动显示的,所以vue-router中利用修改/的path然后重定向到主页的方法就可以了:

      routes: [
        {
          //网站进去的第一个页面
          path: '/',
          // 重定向到/home的url
          redirect: '/home'
        },
        {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home
        },
        {
          // 输入/about时就会跳转到About页面
          path: '/about',
          component: About
        },
      ],
    

    在routes里面配置一个新的对象,path为/(主页,或者直接为空也可以),然后重定向到主页的url

    53、将url的显示模式从hash模式修改到history模式

    在这里插入图片描述

    可以看到vue-router的url默认显示模式是采用的hash显示格式,很显然这不符合我们的常规,所以,我们可以在routes里配置一个mode,将它的默认修改为history,像这样:

    routes: [
        {
          //网站进去的第一个页面
          path: '/',
          // 重定向到/home的url
          redirect: '/home'
        },
        {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home
        },
        {
          // 输入/about时就会跳转到About页面
          path: '/about',
          component: About
        },
      ],
      // 将默认的url显示默认修改为history
      mode: 'history'
    

    54、修改router-link渲染后的标签

    因为默认router-link会被渲染成a标签,但是我们可以给它添加tag属性,值为button或者div或者li就会被渲染成指定的属性,比如:

    <template>
      <div id="app">
        <!--去首页的超链接-->
        <router-link to="/home" tag="button">首页</router-link>
        <!--去关于的超链接-->
        <router-link to="/about" tag="button">关于</router-link>
        <!--显示内容-->
        <router-view/>
      </div>
    </template>
    

    就会被渲染成button,再比如:

    <template>
      <div id="app">
        <!--去首页的超链接-->
        <router-link to="/home" tag="li">首页</router-link>
        <!--去关于的超链接-->
        <router-link to="/about" tag="li">关于</router-link>
        <!--显示内容-->
        <router-view/>
      </div>
    </template>
    

    就会被渲染成li标签。

    其实你直接将router-link里的内容改对应的标签也可以,比如我修改成按钮:

    <template>
      <div id="app">
        <!--去首页的超链接-->
        <router-link to="/home"><button>首页</button></router-link>
        <!--去关于的超链接-->
        <router-link to="/about"><button>关于</button></router-link>
        <!--显示内容-->
        <router-view/>
      </div>
    </template>
    

    55、router-link里的replace属性

    <template>
      <div id="app">
        <!--去首页的超链接-->
        <router-link to="/home" tag="button" replace>home</router-link>
        <!--去关于的超链接-->
        <router-link to="/about" tag="button" replace>about</router-link>
        <!--显示内容-->
        <router-view/>
      </div>
    </template>
    

    给router-link里添加replace属性后那么通过点击该标签产生的history就不能再回溯了。意思就是不能通过history.back()或者history.forward()方法后退和前进。

    56、router-link的动态活跃属性

    因为router-link可以被用作为导航栏,一般用到导航栏就涉及到正在被点击事件,如果正在被点击事件那么这个router-link就拥有一个默认属性class="router-link-active",我们就可以通过给router-link-active这个属性添加样式就可以达到我们想要的效果了。但是如果你觉得这个属性名太长了,你可以自定义,利用:

    <template>
      <div id="app">
        <!--去首页的超链接-->
        <router-link to="/home" tag="button" active-class="active">home</router-link>
        <!--去关于的超链接-->
        <router-link to="/about" tag="button" active-class="active">about</router-link>
        <!--显示内容-->
        <router-view/>
      </div>
    </template>
    

    给router-link添加一个active-class属性,属性值就是你自定义的该标签被选中的名字,只要该标签被选中那么就会有你自定义的这个class属性。但是这有点不方便的就是假如你的router-link标签过多你需要自己一个一个添加active-class,那么你可以尝试在routes里配置linkActiveClass:

      //配置路由和页面的映射关系
      routes: [
        {
          //网站进去的第一个页面
          path: '/',
          // 重定向到/home的url
          redirect: '/home'
        },
        {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home
        },
        {
          // 输入/about时就会跳转到About页面
          path: '/about',
          component: About
        },
      ],
      // 将默认的url显示默认修改为history
      mode: 'history',
      // router-link被选中时添加class属性active
      linkActiveClass: 'active'
    

    这样也可以达到你的router-link被选中时自动添加class属性active,就不需要你一个一个手动添加active-class为active了。

    57、利用vue的内置对象$router来实现router-link

    假如我们不想通过router-link标签来切换页面,我们同时也可以采用监听点击事件然后使用vue的内置对象$router的方式来切换页面。

    <template>
      <div id="app">
    
        <button @click="toHome">首页</button>
        <button @click="toAbout">关于</button>
    
        <!--显示内容-->
        <router-view/>
      </div>
    </template>
    

    在页面里通过button来监听事件,然后在事件里:

    export default {
      name: 'App',
      methods: {
        toHome() {
          // 去/home
          this.$router.push('/home');
        },
        toAbout() {
          // 去/about
          this.$router.push('/about');
        }
      },
    }
    

    利用this.$router.push()来跳转到对应的页面。

    58、利用vue-router实现动态路由

    假如现在我需要一个页面,页面上的内容需要显示的是给用户打招呼,需要在页面上显示用户的用户名,所以这个时候肯定需要动态地获取用户的名字。

    1. 需要先创建一个组件,叫user

      <template>
        <div>
          <h2>你好!{{username}}</h2>
          <p>我是用户界面</p>
        </div>
      </template>
      
      <script>
        export default {
          name: "User",
          computed: {
            // 需要动态获取的
            username() {
                
            }
          },
        }
      </script>
      
      <style scoped>
      
      </style>
      
      
    2. 在路由的routes中引入并配置好这个页面

      
      // 1、引入vue-router和vue
      import VueRouter from 'vue-router';
      import Vue from 'vue';
      
      // 将两个页面导入
      import Home from "../components/Home";
      import About from "../components/About";
      import User from "../components/User";
      
      // 2、用vue.use()全局使用vue-router插件
      Vue.use(VueRouter);
      
      // 3、创建router对象并且导出
      export default new VueRouter({
        //配置路由和页面的映射关系
        routes: [
          {
            //网站进去的第一个页面
            path: '/',
            // 重定向到/home的url
            redirect: '/home'
          },
          {
            // 输入/home时就会跳转到Home页面
            path: '/home',
            component: Home
          },
          {
            // 输入/about时就会跳转到About页面
            path: '/about',
            component: About
          },
          {
            // :冒号后面的内容表示需要动态获取
            path: '/user/:userId',
            component: User
          }
        ],
        // 将默认的url显示默认修改为history
        mode: 'history',
        // router-link被选中时添加class属性active
        linkActiveClass: 'active'
      });
      
      

      在路由的index.js里引入user组件并且在routes中配置好user这个组件,并且在path中加上:userId表示userId是可以动态获取的。

    3. 加上router-link使其能够访问到这个页面

      <template>
        <div id="app">
      
          <!--去首页的超链接-->
          <router-link to="/home" tag="button">主页</router-link>
          <!--去关于的超链接-->
          <router-link to="/about" tag="button">关于</router-link>
      
          <router-link :to="'/user/'+userId" tag="button">我的</router-link>
      
          <!--显示内容-->
          <router-view/>
        </div>
      </template>
      

      在前往user这个页面的router-link里加上v-bind:to(表示动态获取userId),这个userId就从这个页面的data中来。

    4. 在user这个页面中获取到想要显示的内容

        computed: {
            // 需要动态获取username
            username() {
              let userId=this.$route.params.userId;
              console.log(userId);
              if (userId==10){
                return '张三';
              }else {
                return '李四';
              }
            }
          },
      

      最后再在user这个组件的computed里利用this.$route.params.userId获取到刚刚从router-link里动态绑定的userId。

      **Tips:**this.$route.params的作用就是获取到目前活跃的路由页面,也就是正在被点击的这个页面里的参数。

    59、路由懒加载

    因为前端路由的思想就是尽量一次性将前端静态资源获取下来,然后再通过路由来判断跳转到对应的页面。但是有个问题就是假如说前端的静态资源代码过多,那么获取的时长就会很长,给用户带来的体验就很不好。所以,这个时候我们就可以使用路由懒加载。

    懒加载,顾名思义就是用到的时候再加载。因为前端路由可以跳转到不同的页面,所以我们可以尝试再跳转到某个页面的时候再去获取到这个页面的对应的组件代码,这样就不会一次性获取大量的代码,给用户带来不好的体验。做法挺简单的,在路由的index.js里不再采用之前的import User from "../components/User";而是采用const Home = () => import('../components/Home');这样就可以达到通过前端路由跳转到某个页面然后再获取这个页面对应的静态资源了:

    
    // 1、引入vue-router和vue
    import VueRouter from 'vue-router';
    import Vue from 'vue';
    
    // 利用懒加载的方式加载页面
    const Home = () => import('../components/Home');
    const About = () => import('../components/About');
    const User = () => import('../components/User');
    
    // 2、用vue.use()全局使用vue-router插件
    Vue.use(VueRouter);
    
    // 3、创建router对象并且导出
    export default new VueRouter({
      //配置路由和页面的映射关系
      routes: [
        {
          //网站进去的第一个页面
          path: '/',
          // 重定向到/home的url
          redirect: '/home'
        },
        {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home
        },
        {
          // 输入/about时就会跳转到About页面
          path: '/about',
          component: About
        },
        {
          path: '/user/:userId',
          component: User
        }
      ],
      // 将默认的url显示默认修改为history
      mode: 'history',
      // router-link被选中时添加class属性active
      linkActiveClass: 'active'
    });
    
    

    60、vue-router路由嵌套

    有的时候需要在路由里面继续添加路由的话就需要给路由添加子路由了,比如:

      {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home,
          // 子路由组件
          children:[
            {
              path: 'news',
              component: HomeNews
            },
            {
              path: 'message',
              component: HomeMessage
            }
          ]
        },
    

    我给home添加了两个子路由,一个叫news一个叫message(注意前面不能加/),不然就会变成绝对路径,然后再懒加载引入组件就可以了,这样就可以通过/home/news/home/message这两个url进行访问了。此时假如我仍然需要给这个子路由添加一个首页的话就仍然需要利用重定向到news这个path:

      {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home,
          // 子路由组件
          children:[
            {
              path: '',
              redirect: 'news'
            },
            {
              path: 'news',
              component: HomeNews
            },
            {
              path: 'message',
              component: HomeMessage
            }
          ]
        },
    

    虽然我们配置好了路由,但是我们仍然需要在页面上通过router-link点击跳转到对应的页面,然后需要一个router-view来使得内容显示出来。所以我们需要在用到的页面,也就是Home这个页面(组件)里:

    <template>
      <div>
        <h2>{{title}}</h2>
        <p>{{content}}</p>
    
        <router-link to="/home/news">查看新闻</router-link>
        <router-link to="/home/message">查看消息</router-link>
        <router-view/>
    
      </div>
    
    
    
    </template>
    

    注意,这里的路径需要加上绝对路径的全路径。

    61、vue-router利用query来传递对象参数

    有的时候我们想要通过路由来传递参数的时候很多时候可能不止传一个参数,甚至会传递一个对象。这个时候我们就可以利用query来传递:

        <!--利用query传递对象参数-->
        <router-link :to="{ path: '/profile', query: { name: '张三', age:18 } }" tag="button">我的</router-link>
    

    在:to里加上一个对象,然后对象里有路径,和需要传递的对象,然后在另一个页面利用$route.query获取到传递的query对象:

    <template>
      <div>
        <h2>我是profile组件</h2>
        <p>{{$route.query.name}}</p>
        <p>{{$route.query.age}}</p>
      </div>
    </template>
    

    62、利用$router和query来传递对象参数

    假如我们不想使用router-link的方式来切换页面并且传递参数,我们可以使用按钮或者其他任意方式,然后监听事件来切换页面并且在事件里传递参数。

    1. 利用button来绑定事件:

        <button @click="toProfile()">我的</button>
      
    2. 在事件里利用$router跳转页面并且传递参数

        methods: {
          toProfile() {
            this.$router.push({
              // 跳转的路径
              path:'/profile',
              // 传递的对象
              query:{
                name: '张三',
                age: 18
              }
            })
          }
        },
      

      在事件里利用$router的push方法,里面指定path路径和query需要传递的参数对象,就和利用router-link跳转页面并且传递参数的方式一致了。

    63、Vue中router和route的区别

    在上面我们尝试分别利用了:

    this.$route.params
    this.$route.query
    this.$router.push()
    

    那我们就需要搞清楚Vue的内置对象router和route的区别。先讲之前我先给这两个单词取两个名字,router叫路由器,route叫路由(组)。

    export default new VueRouter({
      //配置路由和页面的映射关系
      routes: [
        {
          //网站进去的第一个页面
          path: '/',
          // 重定向到/home的url
          redirect: '/home'
        },
        {
          // 输入/home时就会跳转到Home页面
          path: '/home',
          component: Home,
          // 子路由组件
          children:[
            {
              path: '',
              redirect: 'news'
            },
            {
              path: 'news',
              component: HomeNews
            },
            {
              path: 'message',
              component: HomeMessage
            }
          ]
        },
        {
          // 输入/about时就会跳转到About页面
          path: '/about',
          component: About
        },
        {
          path: '/user/:userId',
          component: User
        },
        {
          path: '/profile',
          component: Profile
        }
      ],
      // 将默认的url显示默认修改为history
      mode: 'history',
      // router-link被选中时添加class属性active
      linkActiveClass: 'active'
    });
    

    在Vue中,我们在index中导出的这整个模块就叫router路由器,然而在router里面配置的routes路由组就是路由的集合。所以不难理解 ,router就是能够控制所有路由的对象,而route就是能够被路由器控制并且跟各个组件(页面)绑定的对象。比如:

        {
          // 输入/about时就会跳转到About页面
          path: '/about',
          component: About
        },
    

    就是一个route。

    所以,我们就可以利用this.$router.push()来控制各个路由之间的跳转,并且在跳转的过程中携带参数。

    而我们就可以利用this.$route.params或者this.$route.query来获得跳转过程中的参数。因为router只负责跳转,跳转完后就失效了,参数都是直接存到各个route路由中。

    在这里插入图片描述

    所以,涉及到跳转页面相关的就使用router,涉及到跳转后需要使用参数的就是用route。

    64、导航守卫

    Vue里的导航守卫就是全局守卫。分为前置守卫(beforeEach)和后置守卫(afterEach)。顾名思义就是在通过导航跳转到某个组件前的离开某个组建后的“守卫”(可以定义相应的事件)。前置守卫和后置守卫是全局守卫,需要在router的index.js里面定义。比如:

    // 前置守卫
    router.beforeEach();
    // 后置守卫
    router.afterEach();
    

    可以常用来判断用户是否登录。

    // 前置守卫
    router.beforeEach(function (to,from,next) {
      // 将网页的标题设置为route里meta里的title
      document.title=to.matched[0].meta.title;
      // 继续跳转到下一个route
      next();
    })
    

    如上所示,使用前置守卫在页面加载前就指定网页的title。

    同理还有后置守卫。前置守卫和后置守卫都是全局守卫,其实还有组件内的守卫,都是大同小异,只是定义的位置可能不同而已。

    65、keep-alive保证组件不被频繁销毁

    在vue中,组件切换后默认是会被销毁的,然后再回到该组件的时候又会被重新创建。然而keep-alive就可以保证组件切换后不会被销毁,那么这样组件切换回来的时候也就不会被重新创建了(因为它本来就一直存在)。

    <template>
      <div>
        <h2>{{title}}</h2>
        <p>{{content}}</p>
    
        <router-link to="/home/news">查看新闻</router-link>
        <router-link to="/home/message">查看消息</router-link>
        <router-view/>
    
      </div>
    
    
    
    </template>
    
    <script>
      export default {
        name: "Home",
        data() {
          return {
            name: '主页',
          }
        },
        computed: {
          title() {
            return '我是'+this.name+'的标题';
          },
          content() {
            return '我是'+this.name+'的内容';
          }
        },
        // 生命周期函数:在组件被创建后调用
        created() {
          console.log('home created');
        },
        // 生命周期函数:在组件销毁后被调用
        destroyed() {
          console.log('home destroyed');
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    
    

    给vue添加两个生命周期函数created和destroyed来观察组件切换前后组件的生存情况。

    <template>
      <div id="app">
    
        <!--去首页的超链接-->
        <router-link to="/home" tag="button">主页</router-link>
        <!--去关于的超链接-->
        <router-link to="/about" tag="button">关于</router-link>
    
        <router-link :to="'/user/'+userId" tag="button">用户</router-link>
    
        <button @click="toProfile()">我的</button>
    
        <!--显示内容-->
        <keep-alive>
          <router-view/>
        </keep-alive>
      </div>
    </template>
    

    只需要给主页的router-view加上keep-alive标签后,尽管组件被切换走了那么这个组件仍然是不会被销毁的,被切换回来的时候也不需要重新创建,因为它本来就没被销毁,一直都存在。

    66、keep-alive的include和exclude属性

    因为默认的keep-alive是将所有的router-view都保持活跃(不被销毁),但是有的时候可能会用到有的组件需要被销毁,有的组件需要不被销毁,所以就需要用到keep-alive的include和exclude属性。

    <template>
      <div id="app">
    
        <!--去首页的超链接-->
        <router-link to="/home" tag="button">主页</router-link>
        <!--去关于的超链接-->
        <router-link to="/about" tag="button">关于</router-link>
    
        <router-link :to="'/user/'+userId" tag="button">用户</router-link>
    
        <button @click="toProfile()">我的</button>
    
        <!--显示内容-->
        <keep-alive exclude="Profile">
          <router-view/>
        </keep-alive>
      </div>
    </template>
    

    在exclude里利用正则表达式匹配组件里的name属性,比如profile组件里的name是Profile,所以keep-alive里就排除掉Profile,意思就是Profile不保持活跃(会被销毁)。

    67、vue应用程序开发的状态管理模式vuex和state

    vuex是什么?

    VueX是适用于在Vue项目开发时使用的状态管理工具。试想一下,如果在一个项目开发中频繁的使用组件传参的方式来同步data中的值,一旦项目变得很庞大,管理和维护这些值将是相当棘手的工作。为此,Vue为这些被多个组件频繁使用的值提供了一个统一管理的工具——VueX。在具有VueX的Vue项目中,我们只需要把这些值定义在VueX中,即可在整个Vue项目的组件中使用。就有点像java开发中的spring容器一样,我只需要把依赖注入进IoC容器里,然后再需要的地方注入就可以了。在vue里同样是一样的,假如有许多组件之间需要同步共同的状态信息,比如:用户登录状态、用户信息等。我们就无法在单一的某个组件里存放这些状态信息,而是转而放到另外一个类似全局对象里存放,而vuex就是这样一个官方插件。

    利用npm安装vuex插件

    npm install vuex@3.0.1 --save
    

    因为vuex会在开发和实际上线的时候都用到,所以–save直接保存而不需要-dev

    在src目录下创建store文件夹

    类似vue-router在src里创建store文件夹,并且在store文件夹里创建index.js

    在index.js里引入vuex并配置然后导出

    // 导入vue
    import Vue from "vue";
    // 导入vuex
    import Vuex from "vuex";
    
    // 利用Vue的use方法使用Vuex插件
    Vue.use(Vuex);
    
    // 利用Vuex.Store类创建store对象
    const store=new Vuex.Store({
      state: {},
      mutations: {},
      actions: {},
      getters: {},
      modules: {}
    });
    
    // 导出store对象
    export default store;
    
    

    跟vue-router类似,同样是导入了Vuex后再利用Vue的use方法使用Vuex插件。需要注意的是Vuex是利用Vuex.Store类来创建store对象的,并且导出。在这个类里,我们需要定义5个对象,分别是:state、mutations、actions、getters、moduls。

    state:存放状态信息(存放变量),同时这个变量是响应式的。

    mutations:存放对状态信息操作的事件;

    下面以对一个变量counter进行操作的例子:

    const store=new Vuex.Store({
      // 存放状态信息
      state: {
        counter: 0
      },
      // 对state进行操作的事件
      mutations: {
        // 对counter+1
        increment(state){
          state.counter++;
        },
        // 对counter-1
        decrement(state){
          state.counter--;
        }
      },
    });
    

    利用increment和decrement对counter进行自增或者自减操作。

    需要注意的是因为state是可以直接在mutations的方法直接作为形参的,所以直接将state写在函数里就可以了。

    在main.js里引入vuex

    import Vue from 'vue'
    import App from './App'
    // 引入store
    import store from "./store";
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      // 注册store
      store,
      render: h => h(App)
    })
    
    

    先利用import引入store,然后在Vue实例里注册store。

    利用$store.state来获取变量

    <template>
      <div id="home">
        <h2>我是home页</h2>
        <h2>{{$store.state.counter}}</h2>
      </div>
    </template>
    

    直接利用$store.state.counter获取state存放的全局变量。

    store中的state就类似vue中的data

    利用this.$store.commit(‘函数名’)来调用函数

    <template>
      <div id="home">
        <h2>我是home页</h2>
        <h2>{{$store.state.counter}}</h2>
        <button @click="increment()">+</button>
        <button @click="decrement()">-</button>
      </div>
    </template>
    
    <script>
      export default {
        name: "Home",
        methods: {
          // 调用store的mutations里的increment方法
          increment() {
            this.$store.commit('increment')
          },
          decrement(){
            // 调用store的mutations里的decrement方法
            this.$store.commit('decrement');
          }
        },
      }
    </script>
    

    通过在组件的methods里定义好方法,然后在方法里利用this.$store.commit('mutations里对应的函数名')来调用全局函数。

    68、利用devtools来跟踪操作

    首先,需要明白Vuex的三大状态:Actions、Mutations和State。

    Actions:异步处理。

    State:状态信息(变量)。

    Mutations:对状态信息的操作。

    同时Vuex的亮点就是可以devtools来跟踪Mutations的操作。假如组件多了之后,你根本无法知道到底是哪一个组件对这个变量进行了操作,所以vue开发了devtools这样一个浏览器插件,来使得我们开发人员更加容易观察到mutations里的函数对state里的变量的操作情况。

    在这里插入图片描述

    其实本来操作函数可以同获取state里全局变量一样使用:

    this.$store.mutations.increment();
    

    来直接调用的,但是这样的问题就是直接利用组件来操作,这样就没有利用commit方法来操作函数,这样就导致无法利用devtools来跟踪函数的使用情况。所以官方推荐利用:

    $store.state					//获取变量
    this.$store.commit('函数名')	   //执行函数
    

    devtools是什么?

    devtools是vue开发的一款浏览器插件,方便对vuex里commit提交的函数进行跟踪。因为一旦组件多了之后根本不知道到底是哪一个组件对state里的变量进行了操作,给我们开发带来了很大的麻烦,devtools就是来解决上述问题的官方浏览器插件。

    需要chrome插件商店进行安装

    在这里插入图片描述

    69、store中的getters

    store中的getters和vue组件的computed计算属性类似,都是讲state中的变量或者data中的变量进行进一步计算然后返回。同样在gettes里定义的时候需要带上state参数:

      getters: {
        // 年龄大于20岁的学生
        moreStudent(state){
          let studentList = state.studentList;
          let returnStudentList=[];
          for (let i = 0; i < studentList.length; i++) {
            if (studentList[i].age>=20){
              returnStudentList.push(studentList[i]);
            }
          }
          return returnStudentList;
        }
      },
    

    然后在需要的地方使用的时候就利用:

        {{$store.getters.moreStudent}}
    

    store中的getters就类似vue中的computed。

    70、store中的mutations和参数传递的两种方式

    因为函数肯定会涉及到参数传递,所以mutations同样也会需要接收参数,在mutations里接收参数就是直接在state后面加参数就可以了:

      mutations: {
        incrementCount(state,count){
          state.counter+=count;
        }
      },
    

    然后再组件里利用:

        methods: {
          incrementCount(count){
            this.$store.commit('incrementCount',count);
          }
        },
    

    commit方法里,另外跟上需要传递的参数就可以了,并且后面跟上的参数,在vue里面叫做payload(载荷)。

    以上方法仅使用于传递的参数只有一个的情况下,假如需要传递的参数是两个及其以上的情况的话,那么就需要采用payload对象的方式进行传递了:

    	incrementCount(){
            let count=10;
            let test='测试';
            this.$store.commit({
              type: 'incrementCount',
              count: count,
              test: test
            });
         }
    

    commit()方法里传递一个payload对象,在payload对象里第一个参数是type(即是mutations里函数的函数名),后面的参数就是想要传递的参数以键名:键值的方式进行传递,然后在mutations里就利用payload来接收:

    	incrementCount(state,payload){
          console.log(payload);
          console.log(payload.type);
          console.log(payload.count);
          console.log(payload.test);
          state.counter+=payload.count;
        }
    

    payload会拿到整个payload对象,payload.type就是刚刚传递的type,count和test同理。

    更倾向于推荐采用直接传递payload对象的方式进行传递,这样易于扩展,没有只能传递一个参数的局限性。

    store中的mutations就和vue中的methods类似。

    71、响应式的给state里的对象添加属性或者删除属性

    什么是响应式?响应式就是你修改了对象里的某个属性之后页面就会跟着立马刷新就是响应式。vue内部已经做好了监听,只要你修改了对应的数据那么页面上的内容立马就会跟着刷新。但是前提是你必须利用vue给的规则来修改页面的数据。规则就是:

    1. 提前在store里初始化好所需要属性;

    2. 在给state中对的对象添加属性时,需要用以下方式:

      Vue.set(Object: Object,key:string|number,vaule)
      

    如果student对象里原本就存在age属性,那么这种修改方式确实能够做到。假如你并没有给student对象里初始化age属性,那么这种方式就不会生效,而必须采用Vue.set()的方法才能生效。

     state.student.age=18;
    
    Vue.set(Object: Object,key:string|number,vaule)
    

    第一个参数是你要修改的对象,第二个参数是你要修改的属性名,第三个参数是你要修改的属性值。比如:

    Vue.set(student,'age',18)
    

    就是将student对象的age属性改为18。添加也是同理,如果你需要给这个对象添加一个它原本不存在的属性也是这样。

    删除对象里的某个属性则是采用:

    Vue.delete(Object: Object,key:string|number)
    

    第一个参数是你要删除的对象,第二个参数是你要删除的对象里的属性名。比如:

    Vue.delete(student,'age')
    

    就是将student中的age属性删除。

    72、store中的actions执行异步操作

    因为devtools的原因,devtools只能跟踪mutations的同步操作,不能跟踪异步操作。而vuex官方推荐异步操作定义在actions里。所以mutations和actions的区别就是,mutations里定义同步操作,actions里定义异步操作。

      actions: {
        // 异步修改学生信息
        actionUpdateStudentInfo(context,payload){
          console.log('actions的payload->',payload);
          console.log('actions的payload的type->',payload.type);
          console.log('actions的payload的params->',payload.student);
         // 利用setTimeout(延时1秒)模拟异步
         setTimeout(function () {
           // 调用mutations里的操作
           context.commit({
             type: 'updateStudentInfo',
             student: payload.student
           });
           // 异步成功后的回调函数
           payload.success();
         },1000);
        }
      },
    

    需要注意的时,actions里的函数的参数必须有context,payload。context是上下文对象,payload是组件里传递过来的参数。利用setTimeout()模拟延时异步操作,然后在setTimeout里利用context.commit()向mutations里发起提交,然后调用mutations里的同步方法(因为actions里拿不到state对象,所以想要操作state中的数据就必须向mutations里发起提交),同样的,利用context.commit(),里面的参数对象就是{type: 'mutations里的函数名',需要传递的参数}

    然后在组件里同样是利用this.$store.dispatch()来调用actions里的方法:

          actionUpdateStudentInfo(){
            let student={
              name: 'test',
              age: 22,
            };
            this.$store.dispatch({
              // type对应actions里的异步操作名
              type: 'actionUpdateStudentInfo',
              // student是要传递的对象
              student: student,
              // success是异步成功后调用的函数
              success: function () {
                alert('异步修改学生信息成功');
              }
            });
          }
    

    同样的,传递的依旧是一个对象参数:

    {
        type: 'actions里的函数名',
        传递的参数,
        自定义的成功后的回调函数(可有可无)
    }
    

    如果需要使用到成功后的回调函数,那么就直接在actions里调用payload里的success方法就可以了。(最好定义一个成功的回调函数,以方便通知异步成功)

    73、mutations和actions的区别

    mutations里定义的是同步操作,actions里定义的是异步操作;

    mutations定义的函数的参数需要有state和payload(因为mutations允许操作state)。actions定义的函数的参数有context和payload(因为actions不能操作state,只有利用context获得mutations里的同步方法);

    组件里使用mutations里的函数是直接利用this.$store.commit()调用(并且传递对象,对象里须定义type和参数),但是组件里使用actions里的函数是是直接利用this.$store.dispatch()调用(并且传递对象,对象里须定义type和参数),然后在actions里利用context.commit()调用mutations里的方法(并且传递对象,对象里须定义type和参数);

    相当于使用mutations只需要利用commit调用指定的mutations里的函数就可以了。但是actions需要利用dispatch调用指定的actions里的函数,但是在actions里的函数里需用利用commit调用mutations里的函数。

    同步操作:commit->mutations。

    异步操作:dispatch->actions->commit->mutations。

    74、store中的mudules

    modules实际上就是相当于在store里再套一个store,然后这个store拥有自己的state、getters、mutations、actions和mudules。

    const store=new Vuex.Store({
      // 存放状态信息
      state: {},
      // 对state进行操作的事件
      mutations: {},
      // 异步操作事件
      actions: {},
      // 类似计算属性
      getters: {},
      // 新的模块(子模块)
      modules: {
          modulesA: {
              state: {},
              mutations: {},
              actions: {},
              getters: {},
          }
      }
    });
    

    为了代码美观,我们可以将子模块对象提到外面(先在外面定义好moduleA和moduleB,然后再在store里声明就好了):

    // moduleA
    const moduleA={
      state: {},
      getters: {},
      mutations: {},
      actions: {}
    }
    
    // moduleB
    const moduleB={
      state: {},
      getters: {},
      mutations: {},
      actions: {}
    }
    
    const store=new Vuex.Store({
      // 存放状态信息
      state: {},
      // 对state进行操作的事件
      mutations: {},
      // 异步操作事件
      actions: {},
      // 类似计算属性
      getters: {},
      // 新的模块(子模块)
      modules: {
        moduleA,
        moduleB
      }
    });
    

    75、module中的state

    module中同样可以定义属于module自己的state:

    // moduleA
    const moduleA={
      state: {
        name: '我是moduleA的name'
      },
      getters: {},
      mutations: {},
      actions: {}
    }
    

    需要注意的是在组件里使用的时候是利用:

        <h2>{{$store.state.moduleA.name}}</h2>
    

    这里特殊的原因是因为在vue内部里是将根state和模块里state归并在一起的,但是模块里的state又是属于moduleA里的:

    在这里插入图片描述

    观察到counter和moduleA是并列的,name又是属于moduleA的,所以需要使用$store.state.moduleA.name

    76、module中的getters

    module中同样也可以定义属于module自己的getters:

    // moduleA
    const moduleA={
      state: {
        name: '我是moduleA的name'
      },
      getters: {
        aName(state){
          return state.name+'moduleA的getters';
        }
      },
      mutations: {},
      actions: {}
    }
    

    需要注意的是在组件里面使用是利用:

        <h2>{{$store.getters.aName}}</h2>
    

    在vue内部也是将所有的getter都归并到一起的,但是getters并没有像state那样需要加上模块名字,而是直接getter的名字。所以尽量子模块和根模块的getter不要重名。同样的:

    // moduleA
    const moduleA={
      state: {
        name: '我是moduleA的name'
      },
      getters: {
        aName(state){
          return state.name+'moduleA的getters';
        },
        bName(state,getters,rootState){
          return getters.aName+rootState.counter;
        }
      },
      mutations: {},
      actions: {}
    }
    

    还可以在getter里利用第二个参数getters获得自己内部的getters,以及第三个参数rootState获得根store的state。

    77、module中的mutations

    module中也可以定义mutations:

    // moduleA
    const moduleA={
      state: {
        name: '我是moduleA的name'
      },
      getters: {
        aName(state){
          return state.name+'moduleA的getters';
        },
        bName(state,getters,rootState){
          return getters.aName+rootState.counter;
        }
      },
      mutations: {
        updateName(state){
         state.name='我是修改后的moduleA的name';
        }
      },
      actions: {}
    }
    

    同样的,需要在组件里利用commit:

    methods: {
      updateName() {
        this.$store.commit({
          type: 'updateName'
        });
      }
    },
    

    在vue内部依旧是根模块和子模块的所有motations都是归并到一起的,所以尽量不要重名。

    78、module中的actions

    同样,在module里也可以使用actions:

    // moduleA
    const moduleA={
      state: {
        name: '我是moduleA的name'
      },
      getters: {
        aName(state){
          return state.name+'moduleA的getters';
        },
        bName(state,getters,rootState){
          return getters.aName+rootState.counter;
        }
      },
      mutations: {
        updateName(state){
         state.name='我是修改后的moduleA的name';
        }
      },
      actions: {
        asyncUpdateName(context,payload){
          setTimeout(function () {
            context.commit({
              type: 'updateName',
            });
            payload.success();
          },1000);
        }
      }
    }
    

    同样的也是需要context和payload参数,同样是利用context的commit方法调用mutations里的函数(只能调用自身的)。然后在组件里也是利用dispatch来使用异步函数:

    	  asyncUpdateName(){
            this.$store.dispatch({
              type: 'asyncUpdateName',
              success:function () {
                alert('异步修改名字成功');
              }
            })
          }
    

    所以,可以得知:actions在vue内部也是所有都归并在一起的。

    79、利用模块化思想抽离store

    在store的index.js因为actions、mutations、getters、modules这些混在一起导致看起来非常的冗余,所以我们可以尝试将它们抽离出去(state不建议抽离)。所以我们需要新建actions.js、mutations.js、getters.js,modules的话就新建一个文件夹:

    在这里插入图片描述

    分别将index.js里的actions、getters、mutations抽离出来,然后再导出,再在index.js里导入:

    actions.js:

    // actions
    const actions={
      // 异步修改学生信息
      actionUpdateStudentInfo(context,payload){
        console.log('actions的payload->',payload);
        console.log('actions的payload的type->',payload.type);
        console.log('actions的payload的params->',payload.student);
        // 利用setTimeout(延时1秒)模拟异步
        setTimeout(function () {
          // 调用mutations里的操作
          context.commit({
            type: 'updateStudentInfo',
            student: payload.student
          });
          // 异步成功后的回调函数
          payload.success();
        },1000);
      }
    };
    // 导出actions
    export default actions;
    
    

    mutations.js:

    // mutations
    import Vue from "vue";
    
    const mutations={
      // 对counter+1
      increment(state){
        state.counter++;
      },
      // 对counter-1
      decrement(state){
        state.counter--;
      },
      incrementCount(state,payload){
        console.log(payload);
        console.log(payload.type);
        console.log(payload.count);
        console.log(payload.test);
        state.counter+=payload.count;
      },
      insertStudent(state,payload){
        console.log('payload->',payload);
        console.log('type->',payload.type);
        console.log('student->',payload.student);
        state.studentList.push(payload.student);
      },
      deleteStudent(state,payload){
        console.log('payload->',payload);
        console.log('type->',payload.type);
        state.studentList.pop();
      },
      insertStudentInfo(state,payload){
        console.log('payload->',payload);
        console.log('type->',payload.type);
        console.log('sex->',payload.sex);
        let sex=payload.sex;
        let studentList=state.studentList;
        for (let i = 0; i < studentList.length; i++) {
          Vue.set(studentList[i],'sex',sex);
        }
      },
      deleteStudentInfo(state,payload){
        console.log('payload->',payload);
        console.log('type->',payload.type);
        let studentList=state.studentList;
        for (let i = 0; i < studentList.length; i++) {
          Vue.delete(studentList[i],'age');
        }
      },
      // 同步修改学生信息
      updateStudentInfo(state,payload){
        console.log('mutations的payload->',payload);
        console.log('mutations的payload的type->',payload.type);
        console.log('mutations的payload的params->',payload.student);
        state.student=payload.student;
      }
    };
    // 导出mutations
    export default mutations;
    
    

    getters.js:

    // getters
    const getters={
      // 年龄大于20岁的学生
      moreStudent(state){
        let studentList = state.studentList;
        let returnStudentList=[];
        for (let i = 0; i < studentList.length; i++) {
          if (studentList[i].age>=20){
            returnStudentList.push(studentList[i]);
          }
        }
        return returnStudentList;
      }
    };
    // 导出getters
    export default getters;
    
    

    moduleA文件夹下的moduleA.js:

    // moduleA
    const moduleA={
      state: {
        name: '我是moduleA的name'
      },
      getters: {
        aName(state){
          return state.name+'moduleA的getters';
        },
        bName(state,getters,rootState){
          return getters.aName+rootState.counter;
        }
      },
      mutations: {
        updateName(state){
          state.name='我是修改后的moduleA的name';
        }
      },
      actions: {
        asyncUpdateName(context,payload){
          setTimeout(function () {
            context.commit({
              type: 'updateName',
            });
            payload.success();
          },1000);
        }
      }
    };
    // 将moduleA导出
    export default moduleA;
    
    

    moduleB文件夹下的moduleB.js:

    // moduleB
    const moduleB={
      state: {},
      getters: {},
      mutations: {},
      actions: {}
    };
    // 将moduleB导出
    export default moduleB;
    
    

    然后再在index.js里集中导入:

    // 导入store的actions、mutations、getters
    import actions from "./actions";
    import mutations from "./mutations";
    import getters from "./getters";
    
    // 导入store的moduleA和moduleB
    import moduleA from "./moduleA/moduleA";
    import moduleB from "./moduleB/moduleB";
    

    最后在index.js里的store里声明:

    // 利用Vuex.Store类创建store对象
    const store=new Vuex.Store({
      // 存放状态信息
      state: {
        counter: 0,
        studentList: [
          { name: '张三', age: 18},
          { name: '李四', age: 22},
          { name: '王麻子', age: 24},
          { name: 'wqk', age: 30},
        ],
        student: {
          name: 'wqk',
          age: 20,
        }
      },
      actions,
      mutations,
      getters,
      modules: {
        moduleA,
        moduleB
      }
    });
    

    所有抽离的思路就是,将它们写在另外的js文件里,然后再导出,最后在需要的地方导入就可以了。

    80、axios的基本使用

    axios是一个基于promise的用于浏览器和node.js的HTTP客户端,比基于XMR的ajax更先进并且实用。

    1. 利用npm安装axios依赖

      npm install axios@0.18.0 --save
      

      因为我们不仅会在开发时用到它,在项目实际上线时也会用到。所以就需要添加–save参数

    2. 在js里导入axios

      // 导入axios
      import axios from "axios";
      

      因为axios是一个第三方库并不是一个vue官方插件,所以就不需要用Vue.use()来全局使用它。

    3. axios的基本使用

      axios().then().catch();
      

      因为axios是基于promise的,所以axios()是网络请求体,then()是成功后的回调函数,catch()是失败后的回调函数。即:

      axios({
        // 请求的url
        url: '',
        // 请求的方式(默认是GET)
        method: 'GET',
        // 请求的参数
        params: {
      
        }
      }).then(function (res) {
        /*成功后的回调函数*/
        console.log(res);
      }).catch(function (res) {
        /*失败后的回调函数*/
      });
      

      axios里需要传递一个参数对象,对象里需要url、method、params等属性。then和catch里需要的参数是函数。

    81、axios发送并发请求

    因为axios是基于promise的,所以和promise.all()类似,axios也可以利用axios.all()发送多个请求然后统一获取一个结果:

    axios.all([
      axios(),
      axios()
    ]).then(function (results) {
      console.log(results[0]);
      console.log(results[1]);
    })
    

    需要在axios.all()里传递axios数组。同样的,在then里获取分别的结果results数组然后进行统一处理,results[0]就是第一个axios的结果,results[1]就是第二个axios的结果。

    // 并发请求
    axios.all([
    
      axios({
        url: '',
        method: '',
        params: {
    
        }
      }),
    
      axios({
        url: '',
        method: '',
        params: {
    
        }
      })
    
    ]).then(function (results) {
      console.log(results[0]);
      console.log(results[1]);
    });
    

    82、axios的配置信息config

    其实在axiox()需要的对象参数是一个config参数,意思就是需要给axios传递配置信息,常见的axios的配置信息如下:

    {
      // `url` 是用于请求的服务器 URL
      url: '/user',
    
      // `method` 是创建请求时使用的方法
      method: 'get', // default
    
      // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
      // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
      baseURL: 'https://some-domain.com/api/',
          
      // `headers` 是即将被发送的自定义请求头
      headers: {'X-Requested-With': 'XMLHttpRequest'},
    
      // `params` 是即将与请求一起发送的 URL 参数
      // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
      params: {
        ID: 12345
      },
          
      // `data` 是作为请求主体被发送的数据
      // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
      // 在没有设置 `transformRequest` 时,必须是以下类型之一:
      // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
      // - 浏览器专属:FormData, File, Blob
      // - Node 专属: Stream
      data: {
        firstName: 'Fred'
      },
    
      // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
      // 如果请求话费了超过 `timeout` 的时间,请求将被中断
      timeout: 1000,
          
       // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
      responseType: 'json', // default
    
      // `responseEncoding` indicates encoding to use for decoding responses
      // Note: Ignored for `responseType` of 'stream' or client-side requests
      responseEncoding: 'utf8', // default
      
    }
    

    baseURL就是请求的url前缀,比如说我原本的url请求是:

    url: 'https://www.baidu.com/api'
    

    我就可以利用baseURL来分割:

    baseURL: 'https://www.baidu.com'
    url: 'api'
    

    同时需要注意的是因为GET请求的特点是从url里获取参数,而POST请求则是通过request body来传递参数。所以当method为get是需要用params传递参数,而method为post是需要用data来传递参数。

    同时axios还支持统一配置默认的baseURL或者headers:

    // 利用axios.defaults来配置相关默认配置(全局配置)
    axios.defaults.baseURL='https:/www.baidu.com';
    
    // 并发请求
    axios.all([
    
      axios({
        url: 'api1',
        method: 'GET',
        params: {
    
        }
      }),
    
      axios({
        url: 'api2',
        method: 'GET',
        params: {
    
        }
      })
    
    ]).then(function (results) {
      console.log(results[0]);
      console.log(results[1]);
    });
    

    等价于:

    axios.all([
    
      axios({
        baseURL: 'https://www.baidu.com',
        url: 'api1',
        method: 'GET',
        params: {
    
        }
      }),
    
      axios({
        baseURL: 'https://www.baidu.com',
        url: 'api2',
        method: 'GET',
        params: {
    
        }
      }),
    
    ]).then(function (results) {
      console.log(results[0]);
      console.log(results[1]);
    });
    

    利用axios.defaults方法就相当于把多个axios的baseURL提出来了而已,前提是这些axios的baseURL必须一样。同样的,利用axios.defaults还可以修改headers等配置参数。

    83、axios的实例

    由于刚才使用的axios.defaults方法将baseURL和timeout写进了defaults里面,意味着整个页面的所有的axios请求都必须使用axios.defaults里的baseURL和timeout,但是一般在真实开发中的网络请求的baseURL很可能不止一个,所以我们就需要有多组baseURL以及对应的timeout,所以这个时候就需要利用axios的实例来创建一组网络请求,这组网络请求里都是用这组设置好的baseURL和timeout:

    // 创建axios实例axiosInstance1
    let axiosInstance1 = axios.create({
      baseURL: 'https://www.baidu.com',
      timeout: 5000
    });
    
    // 创建axios实例axiosInstance2
    let axiosInstance2 = axios.create({
      baseURL: 'https://www.google.com',
      timeout: 10000
    });
    
    // 利用axiosInstance1实例发起请求
    axiosInstance1().then().catch();
    
    //利用axiosInstance2实例发起请求
    axiosInstance2().then().catch();
    

    先分别利用axios.create()方法创建一个axios实例,然后给这个axios实例里传递这个实例所需要的统一参数,然后再分别利用该实例发起请求:

    // 利用axiosInstance1实例发起请求
    axiosInstance1({
      url: 'api1'
    }).then(function (res) {
      console.log(res);
    }).catch(function (err) {
      console.log(err)
    });
    
    //利用axiosInstance2实例发起请求
    axiosInstance2({
      url: 'api2'
    }).then(function (res) {
      console.log(res);
    }).catch(function (err) {
      console.log(err)
    });
    

    分别向自己的实例里传递url、params、data参数然后初始化好自己的then和catch的处理就可以了。

    84、axios实例的封装以及复用

    如果就以普通的axios使用不加以封装的话每个组件里需要用到axios的地方都需要重新导入axios并且创建属于该组件的axios实例,这是不方便的,代码冗余,我们可以创建一个公共的axios实例来供组件使用。在src下创建一个文件夹叫network,然后创建一个request.js在里面实现网络请求的封装。

    // 导入axios
    import Axios from "axios";
    
    // axios请求(config:请求参数,success:成功的回调函数,error:失败的回调函数)
    function request(config,success,error){
      let axiosInstance = Axios.create({
        baseURL: 'http://www.baidu.com',
        timeout: 5000
      });
    
      axiosInstance(
        config
      ).then(function (res) {
        // 利用传递进来的success函数执行res并且回调出去
        success(res);
      }).catch(function (err) {
        // 利用传递进来的error函数执行err并且回调出去
        error(err);
      });
    }
    
    
    // 导出request函数
    export {
      request,
    };
    

    第一步我们需要先导入axios,才能使用axios这个框架。

    第二步需要实例化axios的组件,然后利用组件使用config、success、error分别进行请求配置、成功的回调、失败的回调。注意的是我们需要利用函数来将它们包裹起来,这样才可以在外部使用模块里的东西时才能向模块里传递参数。

    第三步导出这个模块导出,并且导出request函数。

    在需要使用网络请求这个模块里导入:

    // 将request.js里的request函数导入
    import {request} from "./network/request";
    
    // 使用request函数(将config,success,error分别传递进去)
    request({
      url: '/api'
    },function (res) {
      console.log(res);
    },function (err) {
      console.log(err);
    });
    

    使用request函数将config、success、error都传递进去。

    实质上,axios是基于promise的,所以我们可以利用一种更加美观的方式:

    // axios请求(config:请求参数,success:成功的回调函数,error:失败的回调函数)
    function request(config){
      let axiosInstance = Axios.create({
        baseURL: 'http://www.baidu.com',
        timeout: 5000
      });
    
      return axiosInstance(config);
    }
    

    直接返回axiosInstance()这个对象就行了,因为它本身就是一个promise。然后在外部添加上then和catch就行了:

    // 使用request函数
    request({
      url: '/川A.1234B'
    }).then(function (res) {
      console.log(res);
    }).catch(function (err) {
      console.log(err);
    })
    

    最终,我们就利用axios是基于promise的特性完成了对axios实例最优美的封装:

    // 导入axios
    import Axios from "axios";
    
    // axios请求1
    function request1(config){
      // 配置baseURL和timeout等配置信息
      let axiosInstance = Axios.create({
        baseURL: 'http://www.baidu.com',
        timeout: 5000
      });
      // 将axios实例直接返回(因为它本身就是个promise)
      return axiosInstance(config);
    }
    
    // 导出request函数
    export {
      request1
    };
    

    步骤:

    1. 导入axios框架
    2. 编写函数(目的是为了在外部使用的时候能够向里面传递config参数)
    3. 利用axios的create方法创建axios实例,并且配置好该实例的基本配置,如baseURL、timeout、headers
    4. 直接将axios实例作为返回值返回(因为axios实质上就是一个promise,可以丢给外部让外部来写then和catch)
    5. 最后将这个函数导出

    85、axios实例的复用

    因为我们配置多个axios实例的目的就是为了能配置多个baseURL等相关基本配置,所以我们可以多建立几个函数来配置不同的baseURL等相关配置:

    // 导入axios
    import Axios from "axios";
    
    // axios请求1
    function request1(config){
      // 配置baseURL和timeout等配置信息
      let axiosInstance = Axios.create({
        baseURL: 'http://wwww.baidu.com',
        timeout: 5000
      });
      // 将axios实例直接返回(因为它本身就是个promise)
      return axiosInstance(config);
    }
    
    // axios请求2
    function request2(config){
      // 配置baseURL和timeout等配置信息
      let axiosInstance = Axios.create({
        baseURL: 'http://www.google.com',
        timeout: 10000
      });
      // 将axios实例直接返回(因为它本身就是个promise)
      return axiosInstance(config);
    }
    
    // 导出request函数
    export {
      request1,
      request2
    };
    

    并且将它们分别导出。这里配置了两个函数,两个实例里分别配置了不同的baseURL和timeout。在需要使用的组件直接将它们导入:

    // 将request.js里的request函数导入
    import {request1,request2} from "./network/request";
    
    // 使用request函数
    request1({
      url: '/api1'
    }).then(function (res) {
      console.log(res);
    }).catch(function (err) {
      console.log(err);
    });
    
    request2({
      url: '/api2'
    }).then(function (res) {
      console.log(res);
    }).catch(function (err) {
      console.log(err);
    });
    

    利用import将它们分别导入,然后再利用request1和request2两个函数来分别使用两个不同的baseURL和timeout来发送请求了。

    通常项目中可能会用到2-3个baseURL,所以我们只需要配置2-3个函数,然后在外部需要使用axios网络请求的地方直接利用请求需要的那个网络请求的实例就可以了。

    86、axios拦截器

    axios有两个Interceptor(拦截器),一个是request(请求)拦截器;另一个是response(响应)拦截器。

    request拦截器:

      // 利用interceptors.request.use()方法开启request请求拦截器
      axiosInstance.interceptors.request.use(
        function (config) {
          // request请求发起前进行的操作(一般是对config进行操作)
          console.log(config);
          console.log('interceptors.request-->onFulfilled');
          // 因为requset1这个函数是链式编程,所以一定要将config返回出去
          return config;
        },function (err) {
          console.log(err);
        }
      );
    

    利用interceptors.request.use()方法开启request的拦截器,use方法需要两个参数:onFulfilled函数和onRejected函数(实际上就是拦截成功和拦截失败的函数)。request拦截的实质就是在axios的request请求发起前对axios这个请求的conifg可以允许进行一层封装,比如加上额外的headers或者加上特定的携带信息给后端。

    **Tips:**一定要记得将config返回出去。

    response拦截器:

      // 利用利用interceptors.response.use()方法开启response响应拦截器
      axiosInstance.interceptors.response.use(
        function (res) {
          // 一般从服务器里返回的数据由于axios的封装会返回status和headers头
          // 我们可以利用response拦截器过滤掉它们
          console.log(res);
          console.log('interceptors.response-->onFulfilled');
          // 因为我们不需要其他的响应头,所以我们可以只返回res里的data
          return res.data;
        },
        function (err) {
          console.log(err);
        }
      );
    

    由于axios的封装,一般请求的响应response里都会携带一些可能无关的信息,比如:

    在这里插入图片描述

    只有data才是我们只能从后端获取到的数据,所以我们就可以利用response拦截器只将data返回,而将其他无用的信息过滤掉了。

    展开全文
  • 整合springboot+vue.js--安装vue.js

    千次阅读 2019-04-29 20:52:32
    业余时间想整理一个基于springboot2.x+mybatis+redis(二级缓存)+vue.js的小系统,随着工作经验的增加慢慢去完善它 废话不多说,安排一个node.js,回归最老版的命令行编码! vue.js是什么?为什么要使用它? Vue.js...
  • 这是cn.vuejs.org 官网文档的同步版本,本文档最后更新时间:2018-10-30 对应vuejs最新稳定版本:2.5.16 (Vuejs每个版本的更新日志见 GitHub)
  • VUE.JS的本地环境搭建过程

    千次阅读 2019-04-21 12:52:59
    我开始了vue.js的学习,这里留下我整个折腾下来的一些经验吧,毕竟现在网上的教程还不是很详细。下面我们进入正题: 首先我们知道vue.js的使用有两种方式:1.直接引用<script>标签路径即可,或者去官网下载到...
  • vue.js 三种方式安装(vue-cli)

    万次阅读 多人点赞 2018-05-29 07:49:05
    Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。它不仅易于上手,还便于与第三方库或既有项目整合。 ...
  • Vue.js 入门教程

    千次阅读 2018-04-20 15:49:22
    Vue.js 教程Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的渐进式框架。Vue 只关注视图层, 采用自底向上增量开发的设计。Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。MVVM...
  • Vue.js 2.0 快速上手精华梳理

    万次阅读 多人点赞 2017-09-03 19:48:27
    自从Vue2.0发布后,Vue就成了前端领域的热门话题,github也突破了三万的star,那么对于新手来说,如何高效快速的学习Vue2.0呢。http://www.tuicool.com/articles/raaeeiU Vue基础 对于没有接触过es6和webpack...
  • Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。它不仅易于上手,还便于与第三方库或既有项目整合。 ...
  • vue.js 三种方式安装

    万次阅读 多人点赞 2017-12-18 20:17:18
    Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。它不仅易于上手,还便于与第三方库或既有项目整合。 ...
  • <template> <div> <svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" /> </div> </template> <script> import screenfull from '... da
  • Vue第二篇之在HTML中引入Vue.js,快速使用Vue

    万次阅读 多人点赞 2018-10-16 11:19:27
    直接在html文件中引入vue.js开始学习,了解vue的基础指令,和整个vue实例的基础架构。 下载vue.js 地址:https://download.csdn.net/download/qq_31122833/10723310,下载完之后在HTML中加入v...
  • Vue.js Vue.js的官方文档中是这样介绍它的:简单小巧的核心,渐进式技术栈,足以应付任何规模的应用。 简单小巧是指Vue.js压缩后仅有17KB。所谓渐进式(Progressive),就是我们可以一步一步、有阶段性地来使用Vue....
  • 策划列表相关的vue.js可畏的事 资源 官方资源外部资源工作门户社区会议播客官方的例子教程实例书 项目采用vue.js 开源商业产品应用/网站互动体验企业使用 组件&图书馆 用户界面组件 表通知...
  • vue.js介绍及vue.js优缺点

    万次阅读 2018-10-25 20:53:18
    什么是vue.js Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或...
  • Vue.js介绍以及优缺点

    万次阅读 多人点赞 2017-07-25 09:21:05
    一.MVX框架模式了解 MVX框架模式:MVC+MVP+MVVM 1.MVC:Model(模型)+View(视图)+controller(控制器),主要是基于分层的目的,让彼此的职责分开。  View通过Controller来和Model联系,Controller是View和Model的协调...
  • 解决Vue.js not detected的问题

    万次阅读 多人点赞 2018-05-03 20:19:13
    前几天遇到了一个问题,在安装完Vue.js devtools后,打开自己写的一个vue.js网页,发现这个图标并没有亮起来,还是灰色,点击图标显示Vue.js not detected,打开控制台也没有发现有vue的选项 网上找了很多解决方案都...
  • 什么是vue.js?(概念很清楚)

    万次阅读 多人点赞 2017-06-01 15:35:30
    Vue.js新手入门指南最近在逛各大网站,论坛,以及像SegmentFault等编程问答社区,发现Vue.js异常火爆,重复性的提问和内容也很多,楼主自己也趁着这个大前端的热潮,着手学习了一段时间的Vue.js,目前用它正在做自己...
  • 1.请求数据的模块vue-resource ... vue-resource 是官方提供的vue的一个插件 axios fetch-json 2.模块初始化 npm install vue-resource --save 3.基本语法 // 传统写法 this.$http.get('/...
  • vue.js安装步骤教程

    万次阅读 多人点赞 2019-05-03 17:23:18
    取消
  • vue.jsvue.min.js 文件

    千次阅读 2018-07-17 15:10:45
    https://download.csdn.net/download/qq_36688143/10546461
  • 小记:介绍vue.js

    千次阅读 2018-07-17 12:23:05
    vue.js是目前比较热门的前端框架之一。它具有易用,灵活,高效等特点。它也提供一种帮助我们快速构建饼开发前端项目的模式。本次分享主要就是介绍vuejs,了解vuejs的基本知识,以及开发流程,进一步了解如何通过...
  • <script src="../js/vue.js" type="text/javascript" charset="utf-8"> <script src="../js/vue-myPlugin.js" type="text/javascript" charset="utf-8"> <script type="text/javascript"> Vue.use...
  • vue构造、vue组件和vue实例这三个是不同的概念,它们的关系有点类似于Java的继承概念: 关系:vue构造-&gt;vue组件-&gt;vue实例 也就是说不同的vue组件可以共用同一个vue构造,不同的vue实例可以共用同一...
  • mpvue是一款使用Vue.js开发微信小程序的前端框架。使用此框架,开发者将得到完整的 Vue.js 开发体验,同时为H5和小程序提供了代码复用的能力。如果想将 H5 项目改造为小程序,或开发小程序后希望将其转换为H5,mpvue...
  • vue.js2.0完整视频教程12套

    万次阅读 多人点赞 2017-10-27 00:36:43
    0.1智能社vuejs(1-11章全套) 0.2英文版learing vuejs 0.3Vue.js实战小米阅读开发 0.4走进Vue.js2.0 0.5Vuejs教程45节课 0.6Vue.js+Node.js构建音乐播放器 ...10.vue.js实战wm平台 等...就不一一列举 ...

空空如也

1 2 3 4 5 ... 20
收藏数 769,164
精华内容 307,665
关键字:

vue.js

vue 订阅