轻松掌握纯前端js框架---VUE Ⅳ

908 阅读26分钟
 现实生活即使我们不甘,却又不得不去面对,平静的面对成功,坦然的面对挫折,豁达的面对属于自己的人生。心态好的人,是你最大的财富。

本期主要内容

  1. 组件化开发
  2. SPA
  3. 脚手架
  4. 脚手架结构:
  5. ES6模块化开发:
  6. 懒加载:
  7. 为整个脚手架项目配置统一的axios:3步
  8. 项目开发流程:

一. 组件化开发

  1. 什么是组件: 拥有专属的HTML+CSS+JS+数据的可重用的页面独立的功能区域

  2. 为什么: 重用

  3. 何时: 今后只要页面中有一个区域会被反复使用,都要先封装为组件,再反复使用组件

  4. 如何:

    (1). 封装组件: 创建一个组件对象,添加到Vue大家庭中

    Vue.component("组件名",{
     //其实每个组件都是一个功能完整的小快递员,麻雀虽小五脏俱全!
     //所以,一个组件的内容和new Vue()的内容几乎完全一样
     el:"#app",
     template:`HTML片段`, //模板,强调: 组件的HTML片段必须包含在一个唯一父元素内
     data:{ 变量 },
     data(){ //可反复调用,每次调用都返回一个新的保存数据的对象——老母鸡下蛋
     	return { //new Object()
     		变量: 初始值: 
     		... : ...
     	}
     },
     //其余完全一样!
     methods:{ 函数 },
     computed:{ 计算属性 },
     mounted(){ ... }
     ... ...
    })
    

(2). 在页面中使用组件:vue中的组件其实就是一个可重复使用的自定义HTML标签而已

<组件名></组件名>

强调: 因为HTML不区分大小写,所以如果组件名(也就是将来的自定义标签名)包含多个英文单词时,不要用驼峰命名!应该用-分割多个单词!

  1. 原理:

    (1). 当new Vue()扫描到不认识的自定义HTML标签时,会回vue家找有没有同名的组件对象

    (2). 如果找到同名的组件对象,先把template中保存的HTML片段替换页面中组件名标签的位置

    (3). 自动调用一次data(),为本次组件副本创建一个全新的模型对象

    (4). 结果: 在当前组件这个小区域中就形成了一个缩微版的new Vue()对象和绑定关系。

  2. 示例: 创建一个计数器组件,并反复使用:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script>
    //想创建一个计数器组件,反复使用
    Vue.component("my-counter",{
      //组件要反复复制的HTML模板
      //模板中将来哪里可能发生变化: 
      //本例中span的内容可能发生变化,所以用{{n}},初始为0
      //模板中将来哪里可能触发事件: 
      //本例中button可能触发事件
      template:`<div>
        <button @click="minus">-</button>
        <span>{{n}}</span>
        <button @click="add">+</button>
      </div>`,
      //因为页面上需要1个变量
      data(){
        return {
          n:0
        }
      },
      //因为页面上需要两个事件处理函数
      methods:{
        add(){
          this.n++;
        },
        minus(){
          if(this.n>0){
            this.n--;
          }
        }
      }
    })
  </script>
</head>
<body>
  <div id="app">
    <!--页面上反复使用三次计数器组件: -->
    <my-counter></my-counter>
    <my-counter></my-counter>
    <my-counter></my-counter>
  </div>
  <script>
    new Vue({
      el:"#app"
    })
  </script>
</body>
</html>
运行结果: 

  1. 组件化开发:

    (1). 什么是: 拿到一个网页之后,先将页面划分为多个组件区域,分头并行开发多个组件。最后再将所有组件拼成一个页面展现给用户。

    (2). 为什么: 2个主要原因:

    a. 可以多人并行开发一个页面——快!

    b. 松耦合,一个人出错,不牵连大家!

    (3). 何时: 今后所有使用前端框架开发的项目,全都采用组件化开发

    (4). 如何:

    a. 拿到页面先划分有几个组件: 2个原则

     1). 区域划分
     
     2). 是否重用
    

    b. 在页面之外创建独立的组件.js文件,包含组件的HTML+CSS+JS+数据

    c. 创建一个完整的.html文件,将外部的组件.js文件内容都引入到.html文件中,在html文件中指定位置使用组价标签,将多个组件合为一个页面.

    (5). 问题: 有些子组件,只有放在规定的父组件内才有效,才有意义。一旦出了父组件就没有意义了!

    (6). 解决: 其实: vue中的组件大致分为三大类:

    a. 根组件root: 一个页面只有一个,监控整个页面的组件
     new Vue({
     	... ...
     })
     
    b. 全局组件: 可在任何位置反复使用的页面独立功能区域——没有限制Vue.component("组件名", { ... })
     
    c. 子组件: 只能在某个指定的父组件内才能使用的小组件,2步:
     1). 创建子组件对象: 
     var 子组件名={
     	//和Vue.component中的格式完全一样!
     	template:`HTML片段`,
     	data(){ return { 变量 } },
     	... ...
     }
     强调: 因为子组件名是一个js变量,所以,不能用-分割多个单词,必须用驼峰命名
     
     2). 在父组件中添加components属性来限制子组件只能在自己范围内使用
     Vue.component("父组件",{
     	template:`HTML片段`,
     	data(){ return { 变量 }},
     	... ...
     	components:{ 子组件对象名 , ... , ... }
     })
     强调: 但是,父组件中components会将驼峰命名的子组件名自动翻译为-分割多个单词,所以在父组件template中使用子组件标签时,依然要使用-分割
     	比如: components:{  todoAdd,   todoList }
     		   自动翻译 <todo-add>   <todo-list>
    

    (7). 坑: 因为创建父组件时就需要子组件对象了,所以引入时,比如先引入子组件,再引入父组件!

    (8). 示例: 使用组件化开发的方式开发待办事项列表的页面(暂时不含数据)

var todoItem={
  template:`<li>
    1. 吃饭 <a href="javascript:;">×</a>
  </li>`
}
var todoList={
  template:`<ul>
    <todo-item></todo-item>
    <todo-item></todo-item>
    <todo-item></todo-item>
  </ul>`,
  //规定todoItem组件只能在当前todoList组件内才能使用,出了todoList组件外使用会报错
  components:{ todoItem }
  //自动翻译  <todo-item>
}
var todoAdd={
  template:`<div>
    <input><button>+</button>
  </div>`
}
Vue.component(`todo`,{
  template:`<div>
    <h1>待办事项列表</h1>
    <todo-add></todo-add>
    <todo-list></todo-list>
  </div>`,
  //规定todoAdd和todoList两个组件只能在当前todo组件内才能使用,出了todo组件外使用会报错
  components:{ todoAdd, todoList }
  //自动翻译  <todo-add> <todo-list> 
})
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script src="todo-item.js">
    //var todoItem={ ... }) //孙子
  </script>
  <script src="todo-add.js">
    //var todoAdd={ ... }) //儿子
  </script>
  <script src="todo-list.js">
    //var todoList={ ... }) //儿子
  </script>
  <script src="todo.js">
    //Vue.component("todo",{ ... }) //父亲
  </script>
</head>
<body>
  <div id="app">
    <todo></todo>
    <!-- <todo-item></todo-item> 
    报错: <todo-item> - did you register the component correctly
    说明: 出了todo,就找不到todo-item了!只能在todo内使用!-->
  </div>
  <script>
    new Vue({
      el:"#app"
    })
  </script>
</body>
</html>
运行结果: 

  1. 组件间传参:

    (1). 问题: vue中子组件也无权直接使用父组件中的数据变量

    (2). 解决: 父给子传值!

    (3). 如何: 2步:

a. 父组件中:父将自己的变量通过:绑定的方式交给子组件

	父组件对象: {
		data(){
			变量: 值
		},
		template:`<div>
			... ...
			<子组件 :自定义属性名="变量"></子组件>
		</div>`
	强烈建议: 子组件自定义属性名尽量和父组件中变量名保持一致!
	
b. 子组件中: 子组件从父组件放变量的自定义属性中取出父组件给的变量值
	子组件对象:{
		props:[ "自定义属性名", ...  ]  
		data(){
			return { 自己的变量 }
		},
		template:`<div>
			... ...
			//结果: 子组件中即可使用data()中自己的变量,也可以使用props中父组件给的变量。且props中变量的用法和data中自己变量的用法完全一样!只不过来源不同而已!
			... ...
		</div>`
	}

(4). 示例: 使用父给子传参实现todo案例中任务列表和添加功能。

var todoItem={
  //从父组件todoList给的两个自定义属性中取出父组件给的变量值
  props:["task","i"],
  //props中的变量和data中自己的变量用法一样,也可用于绑定和指令等。只不过props来源于外部,而data是内部定义的而已。
  template:`<li>
    {{i+1}}. {{task}} <a href="javascript:;">×</a>
  </li>`
}

var todoList={
  //从父组件给的自定义属性tasks中取出父组件给的tasks变量值
  props:[ "tasks" ],
  //props中的变量用法和自己data中变量用法完全一样,都可用于绑定、指令等。
  template:`<ul>
    <!--根据父组件中tasks数组中的元素个数,反复生成多个<todo-item>子组件-->
    <todo-item v-for="(task,i) of tasks" :key="i" :task="task"  :i="i"></todo-item>
  </ul>`,
  //规定todoItem组件只能在当前todoList组件内才能使用,出了todoList组件外使用会报错
  components:{ todoItem }
  //自动翻译  <todo-item>
}

var todoAdd={
  props:["tasks"],
  template:`<div>
    <input v-model="task"><button @click="add">+</button>
  </div>`,
  data(){
    return {
      task:""
    }
  },
  methods:{
    add(){
      this.tasks.push(this.task);
      //在添加成功后,清空task变量值,也就自动清空了文本框中残留
      this.task="";
    }
  }
}

Vue.component(`todo`,{
  template:`<div>
    <h1>待办事项列表</h1>
    <todo-add :tasks="tasks"></todo-add>
    <todo-list :tasks="tasks"></todo-list>
  </div>`,
  //因为三个子组件都需要访问一个任务数组
  //所以在父组件中统一保存一个任务数组供所有子组件共同使用!
  data(){
    return {
      tasks:["吃饭","睡觉","打亮亮"]
    }
  },
  //规定todoAdd和todoList两个组件只能在当前todo组件内才能使用,出了todo组件外使用会报错
  components:{ todoAdd, todoList }
  //自动翻译  <todo-add> <todo-list> 
})

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <script src="todo-item.js">
    //var todoItem={ ... }) //孙子
  </script>
  <script src="todo-add.js">
    //var todoAdd={ ... }) //儿子
  </script>
  <script src="todo-list.js">
    //var todoList={ ... }) //儿子
  </script>
  <script src="todo.js">
    //Vue.component("todo",{ ... }) //父亲
  </script>
</head>
<body>
  <div id="app">
    <todo></todo>
    <!-- <todo-item></todo-item> 
    报错: <todo-item> - did you register the component correctly
    说明: 出了todo,就找不到todo-item了!只能在todo内使用!-->
  </div>
  <script>
    new Vue({
      el:"#app"
    })
  </script>
</body>
</html>
运行结果:

二. SPA: Single Page Application

  1. 什么是单页面应用:整个应用程序只有一个唯一完成的HTML页面。其它所谓的"页面",其实都变成了组件。所谓的切换页面,其实切换的不是.html文件,而是一个html页面中的组件部分。

  1. 为什么:

  1. 单页面应用缺点: 首屏加载慢:

    (1). 因为第一次请求时,就会把所有页面组件的内容都下载下来!

    (2). 已经解决了:?

  2. 如何: 3大步

(1). 先创建一个唯一完成的HTML页面

a. 是一个基本的支持vue框架的页面结构
b. 如果有公共组件比如bootstrap.css, jquery.js和bootstrap.js,也要在唯一完整的HTML中引入
c. 在<div id="app">内必须写一个<router-view>元素为将来的页面组件占位
d. 之后还要引入所有页面组件和vue-router.js组件,以及保存VueRouter对象的router.js文件
e. new Vue()中除了el:之外,还要添加router成员
	new Vue({
		el:"#app",
		router
	})

(2). 为每个"页面"分别创建一个组件对象保存各自页面的内容: 强调: 每个"页面"组件,必须采用子组件对象方式创建

	var 组件名={ 
		组件内容
	}

(3). 创建路由器对象根据地址栏的变化,自动切换显示不同的"页面"组件: 2步

a. 创建单独的router.js,保存路由字典和路由器对象
b. 创建路由字典数组: 
	//路由字典: 保存一组路径与组件间对应关系的数组
	var routes=[
		{ path:"/相对路径", component: 路径对应的组件对象名},
		... 
	]
c. 创建路由器对象并将路由字典放入路由器对象中
	var router=new VueRouter({  routes })
  1. 优化路由字典:

    (1). 问题: 通常默认首页是不需要输入任何相对路径就可以访问的

     解决: var routes=[
             {path:"/", component: 首页组件}
     			... 
    

    (2). 问题: 如果用户输入错误的路径,不能什么都不显示,应该显示404找不到网页

     解决: 添加一个专门的404页面组件,并引入到页面中。并且在路由字典结尾添加:
     	{path:"*", component: 404组件对象名}
     	*表示除之前路径之外所有错误的路径,都导向404页面组件
    
  2. 公共页头部分: 一般创建为全局组件,并添加到之外,就不会被替换,而保证每个页面加载时都有!

    问题: 有些页面(比如404)不想显示页头

    解决: 页头不要放在唯一完整的HTML页面中,应该只放入那些需要页头的组件中!

  3. 路由跳转:

    (1). 在HTML中写死的方式: 代替

    <a><router-link to="/相对路径">文本</router-link>
    

    (2). 在js程序中也能用程序跳转:

    固定套路: 没有为什么: this.$router.push("/相对路径")

  4. 路由传值:

(1). 修改路由字典中的相对路径,允许一个路径携带参数

	{ path:"/相对路径/:变量名", component:组件名, props:true }
    props: 让路径中的变量值自动进入下一个页面组件,成为props中的一个变量

(2). 上个页面跳转时可使用: /相对路径/变量值

(3). 在下个页面中用props:[ "变量名" ] 获取传过来的变量值

(4). 结果在下个页面组件中可以用变量名用作绑定或js程序都行!

(5). 坑: 一旦一个路径写了变量,则再用不带变量值的路径,就无法进入了!

	比如: path:"/details/:lid"
	将来: /details/5  可以进入
		 /details/10  可以进入
		 /details     禁止入内!导向404
  1. 示例: 制作一个单页面应用,包含首页和详情页

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="js/vue.js"></script>
  <!--引入全局页头组件-->
  <script src="myHeader.js"></script>
  <!--引入所有页面组件-->
  <script src="index.js">
    //var index={ template:`首页内容` }
  </script>
  <script src="details.js">
    //var details={ template:`详情页内容` }
  </script>
  <script src="notFound.js">
    //var notFound={ template:`404:xxx` }
  </script>
  <!--引入SPA核心组件vue-router.js(官方)-->
  <script src="js/vue-router.js"></script>
  <!--引入我们自己创建的路由器和路由字典-->
  <script src="router.js">
    //var routes=[ 路由字典 ];
    //var router=new VueRouter({ routes })
  </script>
</head>
<body>
  <div id="app">
    <!-- <my-header></my-header> -->
    <!--为将来要加载的页面组件占位-->
    <router-view></router-view>
  </div>
  <script>
    new Vue({
      el:"#app",
      //比如将引入的路由器对象添加new Vue()中,才能让new Vue()在扫描时认识<router-view>占位。
      router
    })
  </script>
</body>
</html>

index.js

var index={
  template:`<main>
    <my-header></my-header>
    <h1 style="color:lightGreen">这里是首页</h1>
    <router-link to="/details/5">查看5号商品的详情</router-link><br/>
    <button @click="toDetails">查看10号商品的详情</button>
  </main>`,
  methods:{
    toDetails(){
      this.$router.push("/details/10");
    }
  }
}

details.js

var details={
  //接住地址栏中的lid变量的值(比如设置props为true才能这样用)
  props:["lid"],
  template:`<main>
    <my-header></my-header>
    <h1 style="color:orange">这里是{{lid}}号商品的详情</h1>
  </main>`
}

notFound.js

var notFound={
  template:`<h1 style="color:red">404:你迷路了~</h1>`
}

router.js

var routes=[
  //默认首页
  {path:"/", component:index},
  //想让详情页可以带商品编号参数lid
  {path:"/details/:lid", component:details, props:true},
  //除以上路径之外,其余路径都导向404页面
  {path:"*", component:notFound}
];
var router=new VueRouter({ routes });

myHeader.js

Vue.component("my-header",{
  template:`<header>
    <h1>这里是页头</h1>
    <ul>
      <li><router-link to="/">首页</router-link></li>
      <li><router-link to="/details">详情页</router-link></li>
    </ul>
    <hr>
  </header>`
})

三. 脚手架:

  1. 什么是脚手架代码:已经包含标准文件夹结构以及核心功能代码的半成品VUE框架项目

  2. 为什么: 统一标准化所有VUE项目的结构!

  3. 何时: 今后所有企业项目都在脚手架标准结构基础上继续开发

  4. 如何: 2步:

    (1). 安装用于反复生成脚手架代码的命令行工具(老母鸡)

    a. 测试当前环境是否安装了vue/cli

     命令行: vue  -V
     看到: 'vue' 不是内部或外部命令,说明vue/cli工具没有安装
    

    b. 安装vue/cli

     npm i -g @vue/cli
     只要看到+ @vue/cli@4.4.5说明安装成功,版本号只要4.x以上都行
    

    c. 测试当前环境是否安装vue/cli

     命令行: vue  -V
     看到: @vue/cli 4.4.5 说明安装成功!版本号只要4.x以上都行
    

    (2). 每做一个项目,都要用命令行工具生成一套脚手架代码(老母鸡下蛋)

    a. 在要生成项目的文件夹,地址栏中输入cmd回车,在当前文件夹打开命令行

    b. 运行vue create 项目名 按回车

     1). ?  Your connection to the default npm registry seems to be slow.Use https://registry.npm.taobao.org for faster installation? (Y/n) Y
     
     2). ? Please pick a preset: (Use arrow keys)
     	default (babel, eslint)
     	> Manually select features
     3). ? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection)
     	>(*) Babel 翻译: 脚手架代码中大量使用了ES6和ES7时髦的语法,大部分旧浏览器不支持!所以,最终交付给客户时,都要编译为等效的ES5代码,才能保证大部分浏览器都能正常使用!
     	 ( ) TypeScript  VUE框架和react框架不主要使用ts,所以第三阶段不讲typescript。因为ng框架强制使用typescript,所以typescript第五阶段讲ng框架时再详细讲。
     	 ( ) Progressive Web App (PWA) Support
     	 (*) Router 单页面应用的核心组件,必选
     	 ( ) Vuex 以后必选!
     	 ( ) CSS Pre-processors 如果用less或sass才选
     	 ( ) Linter / Formatter 代码规范和质量检查工具,就算程序没写错,功能可以正常执行,但是如果代码写的不规范或代码质量差,也会报错!
     	 ( ) Unit Testing
     	 ( ) E2E Testing
     4). ? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)  n
     i. 问题: SPA应用中默认使用#/路径方式作为前端项目客户端导航的地址区分方式
     	比如: #/  默认首页
     		 #/details  详情页
     	如果在一个特别长的网页中也想使用锚点地址做页面内导航,就会和SPA应用的#发生冲突!
     ii. 解决: 在new VueRouter({
     			mode: "history",
     			routes
     		  })
     	结果: SPA应用不再使用#/作为客户端导航地址
     	比如: /   默认首页
     		 /details  详情页
     iii. 问题: 浏览器懵逼了!
     	以前: 浏览器看到#/xxx 会发给前端的vue框架解析, 看到/xxx会发给后端服务器接口解析
     	现在: 都用/,浏览器会将所有/xxx都发给服务器端解析,不再发给vue框架解析,就会出错!
     iv. 解决: 今后只要想开启history模式去#,必须请服务器端工程师用特殊的重定向手段协助解决才行!
     v. 所以: 在达内学习时,不要开history模式,将来到了公司,再开history模式。
     5). ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
     	In dedicated config files
     	> In package.json
     6). Save this as a preset for future projects? (y/N)  n
    c. 看到以下内容,说明安装成功:
      🎉  Successfully created project xzvue.
     👉  Get started with the following commands:
    
     	$ cd xzvue
     	$ npm run serve
    

    (3). 脚手架代码已经是一套可以运行的网站代码了,包含了SPA应用三大部分内容和示例网页。用vscode运行:

    a. 如果不用git管理代码,则可以删除.git隐藏文件夹:

     1). 在操作系统文件夹窗口,选顶部菜单: 查看->选项->查看->选中 显示隐藏的文件、文件夹和驱动器,点应用
     2). 进入刚创建的项目文件夹,删除.git文件夹
    

    b. 用vscode打开刚刚命令行创建的项目文件夹

    c. 右键单击package.json文件,选择"在终端中打开"

    d. 当弹出终端窗口(稍等),看到>,输入npm run serve

     1). DONE  Compiled successfully in 3163ms
     	会编译/翻译脚手架中时髦的代码为ES代码,并压缩代码。
     2). App running at:
     	- Local:   http://localhost:8080/
     	启动一个简化版的服务器,临时保存脚手架项目中的网页,让开发人员可以在本地调试网页功能
     	从此,再不会用live server —— 退出历史舞台!
     	今后,只要是vue脚手架项目都用npm run serve运行!
     3). 按住ctrl,点终端中Local后的连接,就可打开示例页面
    

    e. 你不用每次都反复运行npm run serve:

     (1). npm run serve是热编译,只要代码一修改,一保存,自动重新编译!无需手动重新运行命令!
     (2). 已经打开的页面无需刷新,就自动换成修改后的页面了!
    
  5. (选装)安装vetur插件: 2个办法:

(1). 有外网: 点vscode左侧最下方“扩展”按钮,文本框输入octref.vetur,点安装

(2). 没有外网: 下载小程序->在线->VUE->day04学校电脑版vetur插件点vscode左侧最下方"扩展"按钮,点右上方...,选从VSIX安装,在打开的窗口中选择刚下载的vetur.vsix文件即可

  • 小程序:微信搜索web问题速查

四. 脚手架结构:

  1. 回顾: SPA应用3步+1步:

    (1). 创建一个唯一完整的HTML页面,包含

    a. 占位

    b. new Vue({ ..., router })

    (2). 为每个"页面"都创建一个组件对象/文件

    (3). 在专门的router.js文件中创建路由器对象和路由字典

    (4). 如果有共用的页头,会创建全局组件,在需要的页面组件中引入

  2. 脚手架项目结构就是SPA应用标准结构!只不过有的划分的更细致,有的放入专门的文件夹中。——脚手架项目结构绝不是新知识!

    (1). 也有一个唯一完整的HTML页面: 但是被一分为三

    a. 基础HTML部分,保留在了index.html中

     public文件夹/ 将来做项目时
     css文件夹/  包含不是自己写的第三方的压缩过的css文件,比如bootstrap.min,css
     js文件夹/ 包含不是自己写的第三方的压缩过的js文件,比如: 
     	jquery-1.11.3.min.js
     	bootstrap.min.js
     imgs文件夹/ 包含网站所需的所有图片
     index.html 唯一完整的HTML页面
    
	b. <div id="app">
		<router-view>
		被单独放在src/App.vue文件中

c. new Vue() 也被单独放在src/main.js文件中

d. 将来运行时,先会用App.vue中的

```
<div id="app"><router-view>自动代替index.html中的空的<div id="app">,并用main.js中的new Vue()加载router,扫描index.html中的<div id="app">
e. public/index.html + src/App.vue + src/main.js 合起来等效于昨天SPA中的index.html
```

(2). 也要为每个“页面”分别创建组件对象/文件,只不过

a. 所有页面组件的文件都集中放在src/views文件夹中,

b. 且每个页面都是一个.vue文件,而不是一个.js文件。每个.vue文件包含三部分:

	1). <template></template>内,包含页面组件的HTML片段
	2). <script></script>内,包含页面组件的对象 —— 和昨天讲的页面组件对象相比,除了没有template属性之外,其余完全一样!
	3). <style></style>内,包含这个页面组件所需的所有css定义。

c. 运行时: 也会根据地址栏变化,将对应组件的<template>中的html代替index.html中的<router-view>位置。用<script>中页面组件对象监控<template>中HTML内容的变化实现vue绑定和交互功能。

(3). 也会在专门的router.js文件中创建路由字典和路由器对象: 只不过:

	    a. 必须放在src/router文件夹/index.js
	    b. 内容几乎没变!
	    c. 除了增加了懒加载功能(下午讲)

(4). 如果有公共的页头组件,也应该创建为全局组件:有点变化! 2步

   a. 在src/components文件夹下,新建.vue文件保存全局组件的<template>+<script>+<style>
	        组件们
		问题: 暂时只是一个普通的子组件对象,还不是Vue家的全局组件
	b. 在main.js中,new Vue()之前,2步: 
		1). 引入src/components/全局组件.vue文件,
		2). 再将引入的全局组件对象用Vue.component()函数转化成一个Vue大家庭中的全局组件。
	c. 结果: 在项目任何一个组件中的<template>中使用<组件标签名>来引用全局组件
  1. 样式冲突:

(1). 问题: 所有.vue中的<style>中的css样式最终会被编译到一个大的.css文件中集中保存,默认所有页面其实是共用这批样式的!极容易造成污染!

(2). 解决: 如何避免组件间样式冲突: 高频笔试题

	a. 好的解决办法: 2步: 
	
		1). 在创建组件时,就为组件的唯一父元素定义一个专门的class名!跟别的组件都不一样!
		2). 从此一个组件下的所有css选择器,都要以这个组件的统一class名开头
		
	b. 偷懒的,不一定总是有效的方法: 1步
	
		1). <style scoped>...</style>
		2). scoped属性自动保证当前组件内的样式一定和别的组件的样式不冲突!
		3). 原理: 
		i. scoped会给当前组件随机添加一个自定义属性
			<template>
				<div  afasdfsdaf >
					... ...
		ii. 然后自动给<style>中每个选择器前加上这个自定义属性选择器
			<style>
				[afasdfsdaf]>xxx{ ... }
				[afasdfsdaf]>xxx{ ... }
		iii. 因为不同组件随机生成的自定义属性名不同,所以,也可避免组件间样式冲突
		iv. 问题: 随机生成的自定义属性名没有意义,不可维护。不如自己主动定义的class名好用。
		

五. ES6模块化开发:

  1. 什么是模块: 封装一个事物的属性和功能的对象

  2. 在Vue脚手架中,每个.vue文件默认也都是一个模块对象

  3. 一个模块对象可以随意被其它的模块或文件引入,如果一个模块中想使用另一个模块的内容和功能,都用import引入

    import 自定义对象名 from "相对路径/文件名.vue"

    结果: 将路径所指的.vue文件中的内容,打包为一个对象,引入当前文件中。

    强调: 不管原来模块叫什么名字,from前的对象名是可以任意自定义的!

  4. 抛出模块对象:

    (1). .vue文件在不包含js内容时,默认就是一个模块对象,可被别人import引入

    (2). 如果.vue文件中包含了组件对象(js代码),则必须用export default { ... 组件js内容 ...}抛出,外人才能用import引入使用

六. 懒加载:

  1. 问题: 旧单页面应用中,首屏加载时,只能一次性将所有页面组件,打包为一个大的js文件,下载下来。不管用户是否会看除首页之外的其它页面,都会全部下载所有页面内容。——首屏加载极慢,且不划算

  2. 解决: 懒加载

  3. 懒加载: 首屏只加载首页的内容(懒),之后,每请求一个新页面,才临时下载新页面组件的内容。

  4. 好处: 首屏加载快

  5. 缺点: 后续页面加载稍慢!

  6. 其实: 2种懒加载:

    (1). 异步延迟下载:当加载首页组件内容时,同时在底层发送异步请求,在不影响当前页面使用的情况下,悄悄的下载其余页面的内容

    a. 优点: 当用户访问下个页面时,可能下个页面的组件已经提前异步下载好了!不影响后续页面的加载速度

    b. 缺点: 偷跑流量!在用户不知道的情况下,多下载了内容。

    c. 如何: 只在router/index.js中

     1). 除首页之外的其它页面组件,不要提前import引入!
     2). 路由字典中,除首页之外的其它页面,都可以采用:
     {
     	path: '/about',
     	///* webpackChunkName: "将来独立下载的js文件名" */不能删除,因为这是在为将来独立打包的js文件自定义文件名。
     	component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
     	//结果: 不再把About.vue的内容和大js打包在一起。而是单独生成一个about.js文件
     	//下载时,异步单独下载about.js文件
     }
    

    (2). 完全懒加载:只有当用户确实访问到这个页面时才动态加载这个页面的js。如果用户不访问。则永远不加载该页面.js

    a. 优点: 不会偷跑流量

    b. 缺点: 后续页面加载稍微慢

    c. 如何:

     1). 以上两步还是要做
     2). 多做一件事: 在脚手架项目根目录创建一个文件vue.config.js,在其中添加如下代码: 
     module.exports={
     	chainWebpack:config=>{
     		config.plugins.delete("prefetch") //取消异步延迟下载
     	}
     }
    

    d. 结果: 所有懒加载的页面.js文件都不会提前异步延迟下载。只有用户访问这个页面对应的地址时,才临时下载页面组件.js。

七. 为整个脚手架项目配置统一的axios:3步

  1. 安装axios: npm i -save axios

    看到: + axios@0.19.2 说明安装完成

    结果: node_modules文件夹中多出一个axios文件夹

  2. 引入并配置axios模块: 在main.js中, new Vue()之前

import axios from"axios"//引入node_modules中的模块,什么路径/都不用加!

//配置服务端基础地质: 

axios.defaults.baseURL="http://xzserver.applinzi.com"
  1. 将配置好的axios对象放到Vue的原型对象中!

    (1). 问题: 我们希望在所有页面组件或子组件或全局组件中都能随处使用axios对象发送ajax请求

    (2). 原理: 其实,无论根组件newVue(),还是全局组件,还是页面组件,还是子组件,底层都是Vue类型的子对象。只不过我们看不见new而已!——信任!

    (3). 解决: 所有vue的孩子共用的东西,都要放在Vue的原型对象中 //强行给Vue.prototype添加一个axios属性,值为配置好的axios对象 Vue.prototype.axios=axios;

  2. 结果: 所有组件中,都可用this.axios发送ajax请求!

八. 项目开发流程:

  1. 先在public文件夹中准备好第三方的css和js,以及项目要用的所有图片,并在index.html页面顶部引入第三方的css和js

  2. 如果有整个项目每个页面都需要的基础的css重置代码,应该放在App.vue中的style中!

    因为App.vue相当于所有页面的一个外壳!

    所有页面将来加载时,都会替换App.vue中<router-view>成为App.vue中的一部分所有公共的css代码只要放在App.vue中,所有页面将来都会共用!

  3. 在src/views文件夹下为每个页面创建独立的.vue文件:

	<template>
  		这个页面的HTML片段(唯一父元素!)
	</template>
	<script>
		export default {
  			就是以前的页面组件对象, 当前页面所需的所有变量,函数,ajax请求等!
			props:[],
			data(){
				return {
					变量
				}
			},
			methods:{
				函数
			},
			computed:{
				计算属性
			},
			mounted(){
				this.axios.get(...).then(result=>{ ... })
			}
		}
	</script>
	<style scoped>
		当前页面所需的css
	</style>
  1. 在src/router/index.js中,修改路由字典,换成我们网页的名称和路径!

    (1). 引入我们自己页面的模块对象

    (2). 修改routes中的路由字典

  2. 先在src/components文件夹中创建页头组件.vue文件,还要在main.js中引入页头组件,并转化为全局组件。最后在需要加载页头的位置使用页头组件标签加载页头内容即可。

  3. 在每个页面组件中发送axios请求,获取数据,绑定页面