ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

万字流行前端框架Vue入门级教程 打造属于自己的网页

2021-10-17 18:31:53  阅读:178  来源: 互联网

标签:教程 Vue 渲染 DOM 入门级 webpack 组件 路由


流行前端框架Vue 入门级教程

1 什么是Vue?

Vue是一套用于构建用户界面的渐进式框架,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

2 Vue基本属性

基本属性含义解释
elDOM元素该属性决定了该Vue实例被挂载到哪个DOM元素上,今后该DOM元素由此Vue实例直接管理
data数据该属性用于存储数据,既可以由开发者直接定义,也可以从网络或服务器动态加载
methods方法该属性用于在Vue对象中定义方法
computed计算属性计算属性是封装了复杂逻辑函数的属性,用于简化属性使用,增强可读性和可维护性
components局部组件该属性用于声明该Vue实例可以使用的局部组件
templateHTML模板该属性决定该实例渲染的HTML结构,模板将会替换挂载的元素,挂载元素的内容都将被忽略,除非模板内容有分发插槽
props配置该属性用于接收来自父组件的数据
表2.1

注:
计算属性与方法的区别:前者基于缓存的。只在相关响应式发生改变时计算属性才会重新求值,即只要响应式不变,多次访问计算属性会立即返回之前缓存的计算结果,而不必再次执行函数;而每次访问方法都会重新执行函数,从效率来看,计算属性优于方法。

3 Vue基本指令

基本指令含义举例
v-once声明元素仅渲染一次,而不随着数据动态加载<h2 v-once> {{cnt}}</h2>
v-html声明以html格式解析渲染字符串<h3 v-html=“url”></h3>
v-text声明以纯文本形式解析字符串,该指令与mustache语法{{}}作用相似,但后者内部可书写简单JS表达式,应用更灵活<h3 v-text=“url”></h3>
v-pre跳过该元素及其子元素的Vue编译过程,跳过大量无指令节点会加快编译<!-- 仍显示{{url}} --><h3 v-pre>{{url}}</h3>
v-bind 声明标签中某些属性动态绑定到Vue实例的属性、对象<a :href=“url”>百度</a><a v-bind:href=“url”>百度</a>
v-on @绑定事件监听器<button @click=“add”>+</button>
v-if v-else-if v-else条件渲染(不符合条件的元素及其子代脱离当前HTML文档不渲染)<p v-if=“mark>=90”>优</p><p v-else-if=“mark>=80”>良</p><p v-else>合格</p>
v-show切换元素CSS样式的display属性
v-for遍历数组或对象<li v-for="(index,key,value) in obj">{{index}}.{{key}}:{{value}}</li>
v-model在表单控件(value属性)或组件上创建双向绑定<input type=“text” v-model=“msg”>
v-slot #设置作用域插槽或具名插槽<template #btn><span>左span</span></template>
表3.1

注:
1、黄色标记的是对应指令的语法糖

2、使用v-on指令时,若监听器无参数时可省略();若监听器有参数但省略()则默认第一个形参为事件对象;若监听器有多个参数且包含事件对象,需要用$修饰符,例如:

<button @click="test(123,$event)">测试</button>
test(num,e){
	console.log(num,e);
}

v-on指令主要修饰符如表3.2所示,示例代码:

<button @click.stop.once="test(123,$event)">测试</button>
<button @keypress.enter="test">测试</button>
序号修饰符含义
1stop停止冒泡
2prevent阻止默认行为
3once只触发一次
4keycode || keyAlias键值或键别名,声明该事件仅由特定键触发
表3.2

3、使用v-for指令遍历数组时,数组的部分方法可以触发响应式渲染,例如push()、pop()、shift()、unshift()、splice()、sort()、reverse()

4、v-model指令主要修饰符如表3.3所示。

序号修饰符含义
1lazyv-model默认在每次input事件触发后同步输入框的值与数据。该修饰符修改触发事件为change
2number将输入转换为数值类型
3trim过滤输入字符串的首末空白字符
表3.3

4 组件化

Vue组件化提供了一种抽象,可以开发出一个个独立可复用的组件来构造复杂的整体应用,这个角度下,任何应用都会被抽象成一棵组件树。组件分为全局组件——可以在所有Vue实例下使用;局部组件——仅在声明局部组件的Vue实例下使用。一个组件可以理解为一个Vue实例,享有Vue实例的所有属性。
在这里插入图片描述

图4.1

4.1 创建组件

4.1.1 注册全局组件

Vue.component('m_cpn',{
    template: '#cpn',
    data(){
        return {
            title: '我是标题',
            text: '我是内容'
        }
    }
});

4.1.2 注册局部组件

components:{
    m_cpn: {
        template: '#cpn',
        data(){
            return {
                title: '我是标题',
                text: '我是内容'
            }
        }
    }
}

注册组件时即将组件名(上面示例中为m_cpn)与HTML模板(上面示例中为cpn)绑定。必须指出,若在组件内部注册局部组件,则此局部组件为该组件的子组件。组件必须挂载在某个Vue实例下,否则无法生效

4.2 组件通信

组件对象自身的固有数据存储在data属性中,与Vue对象不同在于,组件的data属性必须是函数,且该函数返回一个存储数据的对象,以使每个组件使用独立的内存空间,防止组件间数据的相互影响与耦合

在这里插入图片描述

图4.2

(1) 父组件传递数据给子组件——props属性

props属性通常是一个对象,内部包含数据对象,数据对象则支持一系列属性:① type:接收数据类型验证。可以指定type为JS内置类型,如String、Number等,也可为自定义数据结构;② default:未接收传参时的默认数据。当type为Array或Object类型时,default必须为函数;③ required:为true时强制从父组件接收参数,否则报错。

父组件还可以通过两个属性访问子组件的数据:① $children:返回所有子组件对象组成的数组,但实际应用中子组件下标值不恒定,故一般不使用该属性;② $refs:通过$ref.x返回ref属性为x的子组件对象。

(2) 子组件传递数据给父组件——自定义事件

① 子组件中自定义事件,并在需要的时候将数据嵌入该事件发射到父组件

this.$emit('add-info',this.mBooks[index],index);

其中add-info即为子组件的自定义事件,后续参数为绑定事件的各个数据。

② 使用子组件的v-on指令,绑定自定义事件到父组件接收函数

<m_cpn :m-books="books" :m-msg="msg" @add-info="addClick"></m_cpn>

其中addClick即为父组件接收函数,缺省()时按顺序传入add-info发射的数据,接收函数内部访问子组件数据时,亦要求按发射顺序接收。类似地,子组件可通过$parent属性访问父组件,但会增强代际耦合,不建议使用。

5 Vue底层原理

5.1 虚拟DOM

如图5.1所示,浏览器渲染引擎工作流程大致分以下步骤:

(1) 使用HTML分析器分析HTML元素,构建DOM树;
(2) 使用CSS分析器分析CSS文件和元素行内样式,生成页面的样式表;
(3) 将DOM树和页面样式表关联起来,构建渲染树(Render树)。由于每个DOM节点都有attach方法——接受样式信息,返回渲染对象,因此该过程又称为Attachment;
(4) 浏览器根据Render树布局,为Render树上每个节点确定在显示屏上的精确坐标;
(5) 调用每个DOM节点的Paint方法,绘制页面。

在这里插入图片描述

图5.1 浏览器渲染引擎渲染流程

用原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍渲染流程,因此操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。虚拟DOM是渲染真实DOM之前,在内存中的JS对象,其设计是为了改善浏览器性能,因为操作JS对象比DOM节点速度快,这样可以避免无谓的重复渲染,而只将最终效果反映到网页。在网页数据更新时会先对虚拟DOM进行打补丁(patch)和重渲染,再一次性渲染到真实DOM。

在这里插入图片描述

(a) 插入节点

在这里插入图片描述

(b) 遍历插入

在这里插入图片描述

(c) 链表式插入
图5.2 DOM Diff算法示例

网页的变化本质上是DOM节点的变化,因此虚拟DOM的核心之一在于新旧DOM树的比较,称为DOM Diff算法。DOM Diff算法只对DOM树的变化部分进行渲染,而对其余部分不作改动,避免遍历,从而大大提高渲染效率。如图5.2所示,DOM Diff只需变化新插入的节点,这意味着虚拟DOM树并非数组类型的顺序数据结构,而是链表型的无序数据结构,因此要高效应用虚拟DOM,需要为DOM节点指定key属性。

5.2 Vue运行流程

在这里插入图片描述

图5.3 Vue运行流程

可以看出在Vue架构中,最终渲染都由渲染函数完成。运行Vue框架有两种模式:
(1) runtime-only:不包含模板编译阶段;
(2) runtime-compiler:包含模板编译阶段。

// runtime-only
new Vue({
  el: '#app',
  render: h => h(App)
})

// runtime-compiler
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>'
})

runtime-only运行时不包含模板编译阶段,所有模板<template></template>只能在.Vue文件中借助Vue插件vue-template-compiler完成开发时编译,在项目打包时模板只已render函数形式存在于工程中,运行时不会二次编译,因此runtime-only运行更快、项目更轻,但该模式下<template></template>不能存在于非.Vue文件中,否则无法渲染。

runtime-compiler函数不限制模板存在的文件形式,但模板仅在运行时编译,性能低于runtime-only模式。

5.3 生命周期钩子

Vue中生命周期指一个Vue实例从创建到销毁的全部过程,如图5.4所示。在Vue实例的生命周期中有很多特殊的时间节点,且往往需要在这些时间节点执行一定的逻辑。封装了这些逻辑的函数称为钩子函数。钩子函数不同于回调函数,前者在事件发生的第一时间激活,后者在事件发生后激活。

常见的生命周期钩子函数如表5.1所示。

在这里插入图片描述

图5.4
序号生命周期钩子含义
1beforeCreate()实例初始化之后,数据观测和事件配置之前调用,此时组件的options还未初始化
2created()实例初始化且完成options配置后,挂载开始前调用,$el属性还未初始化
3beforeMount()挂载阶段开始后,渲染网页前调用,实例已完成模板编译,生成虚拟DOM树(此时$el为VDOM属性)
4mounted()实例挂载完成,渲染到网页后调用
5beforeUpdate()数据更新前调用,由于VDOM还未打补丁,此时进一步更改状态不会触发重渲染
6updated()数据更新后使用
7beforeDestroy()实例销毁前调用,此时仍可访问实例及其子代的各属性
8destroyed()实例销毁后调用
9activated()被keep-alive缓存的组件激活时调用
10deactivated()被keep-alive缓存的组件停用时调用
表5.1

6 Vue-cli脚手架

6.1 Webpack工具

6.1.1 基本配置

在大型前端工程中,模块化有助于降低代码耦合性,防止功能冲突。前端模块化的方案有AMDCMDCommonJSES6等,其中除ES6外的模块化开发方案都需要特定的运行环境。webpack是一个基于Node.js的现代JS应用的静态模块打包工具,统一了各模块化方案,并且可以自动处理模块间的相互依赖。

对于新工程,webpack打包的流程如下:

npm init
// 安装本地webpack包(@指定版本)

npm install webpack@3.5 --save-dev

// 安装全局webpack包
// npm install webpack -g
npm install

为便于不同项目的管理,通常在每个工程下安装局部webpack包,防止版本冲突。但终端执行webpack却会使用全局webpack包,因此要在产生的package.json中配置webpack启动项:

  "scripts": {
    "build": "webpack --config ./build/prod.config.js",
    "dev": "webpack-dev-server --config ./build/dev.config.js --open "
  },

此时运行npm run build即可进行生产模式下项目的打包。

6.1.2 转换器Loader

原生webpack只能处理JS文件及其依赖,但前端开发中还有CSS、Vue文件等,此时需要webpack转化器loader对其他格式的文件进行扩展,大部分文件的转化器可在webpack官网查询。以安装CSS、Vue扩展转换器为例:

// 安装CSS loader
cnpm install style-loader --save-dev
cnpm install css-loader --save-dev
// 安装Vue(运行时依赖,不加-dev)
cnpm install vue@2.6.14 –save
// 安装Vue loader
cnpm install vue-loader vue-template-compiler --save-dev

在配置文件.config.js中作如下配置:

module: {
    rules: [
      {
        test: /\.css$/,                                 // 使能CSS打包
        // 使用多个loader时,从右向左读
        // css-loader: 加载CSS文件
        // style-loader: 将样式加载到DOM
        use: ['style-loader', 'css-loader' ]
      },
      {
        test: /\.vue$/,                                 // 使能vue打包
        use: ['vue-loader']
      }
    ]
  },
resolve: {
    alias: {
      // 引入vue
      'vue$': 'vue/dist/vue.esm.js'
    }
  }

6.1.3 插件Plugin

webpack插件是对webpack现有功能的各种扩展,使webpack使用更方便,常用插件如表6.1所示。

名称功能
HtmlWebpackPlugin自动生成项目html文件(可指定生成模板),并将打包的js文件以<script>标签形式插入到该html文件的<body>中
UglyfyJsPlugin压缩打包后的js文件,减小项目体积,通常用于生产模式
表6.1 webpack常用插件

下面给出配置扩展插件的实例。

安装插件:

cnpm install html-webpack-plugin --save-dev
cnpm install uglifyjs-webpack-plugin@1.1.1 --save-dev

配置.config.js:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const webpack = require('webpack');
const uglifyJsWebpack = require('uglifyjs-webpack-plugin');

module.exports = {
  ...
  plugins:[
    // vue-loader15以上的版本需要此插件解析.vue文件
    new VueLoaderPlugin(),
    // 版权声明
    new webpack.BannerPlugin('@Copyright Winter 2018 - 2021'),
    // 打包html到发布文件夹dist
    new HtmlWebpackPlugin({
      template: 'index.html'                           // html模板
    })
  ],
  // 压缩JS代码
  optimization: {
      minimizer: [new uglifyJsWebpack()],
  }
}

6.1.4 搭建本地服务器

安装:

cnpm install webpack-dev-server --save-dev

配置.config.js:

// 服务器配置
devServer: {
  // 指定提供本地服务的文件夹
  static: {
    directory: path.join(__dirname, '../dist'),
  },
  // 实时监听
  compress: true,
  // 指定服务器端口
  port: 8080
}

6.2 vue-cli

在大型项目开发时,需要使用Vue-CLI辅助完成代码目录结构、项目结构的部署、热加载、单元测试、插件与与依赖安装等配置

在Vue-CLI中通过以下指令初始化项目:

// Vue-CLI 2.x
vue init webpack project-name
// Vue-CLI >= 3.0
vue create project-name

以Vue-CLI 4.5为例介绍主要代码结构,如表6.2所示。

序号文件含义
1dist最终打包发布的生产版本应用
2node_modulesnpm加载的项目所需要的各种依赖模块
3public静态资源目录,如图片字体等(Vue-CLI 2.x中为static)
4src开发源目录
5assets放置一些图片,如logo等
6components组件文件
7router路由配置
8App.vue项目入口组件,构建了与其他组件的关联关系
9main.js项目入口文件,配置依赖
10.browserslistrc浏览器适配性配置
11.gitignoreGit提交项目的忽略文件
12babel.config.js检测ES6语法,并转化为ES5以提高浏览器兼容性
13package-lock.json项目依赖的实际版本信息
14package.json项目依赖的版本要求信息
表6.2

6.3 Vue-router

6.3.1 前端路由与后端路由

路由,指通过互联网将信息从源地址传输到目的地址的活动。

Web应用开发主要经过三个阶段:

(1) 后端渲染阶段

在这里插入图片描述

图6.1

后端渲染主要指:用户在前端触发URL,后端服务器通过正则匹配到该URL对应的网页,并将渲染好的网页返回到前端展示给用户的过程。这种由后端处理URL与HTML页面间映射关系的过程称为后端路由

早期的Web开发均采用后端渲染的形式,这种方式造成页面模板与数据混杂、前后端分工不明确,不利于开发与维护。

(2) 前后端分离阶段

在这里插入图片描述

图6.2

前后端分离主要指:后端API服务器指提供数据服务,静态资源服务器存储由前端提交的网页。用户在前端触发URL会从静态资源服务器下载相应资源并渲染到浏览器,在当前页面通过Ajax技术激活后端API并请求到数据,通过JS动态渲染到网页中。前后端分离使前端专注于交互与可视化,后端专注于数据与API设计,且对多种形式的前端(如移动端、PC端),仅需一套后端API即可。

(3) 前端渲染阶段

前端渲染又称单页面富应用(Single Page Application, SPA),主要指:浏览器会首先从静态资源服务器下载该Web应用的HTML模板(唯一)和整套CSS、JS文件到本地,用户在前端触发URL,浏览器会抽取CSS、JS中属于当前服务的部分渲染到网页。这种由前端处理URL与HTML页面间映射关系的过程称为前端路由。本章路由指前端路由。

在这里插入图片描述

图6.3

6.3.2 实现前端路由

实现前端路由主要分为以下步骤:

(1) 路由管理器index.js中配置路由映射表

const routes = [
  {
    path: '/',
    redirect: '/home',         // 重定向
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
    children: [
      {
        path: 'news',
        component: () => import('../views/HomeNews')      // 路由懒加载
      },
      {
        path: 'message',
        component: () => import('../views/HomeMessage')
      }
    ] 
  },
  {
    path: '/user/:id',
    name: 'User',
    component: () => import('../views/User.vue')
  }
]

注意:

① 采用组件化开发,一条前端路由对应渲染一个组件(可嵌套其他组件)。
② 打包构建Web应用时,JS包通常体积较大,影响页面加载。因此Vue提供了一种路由懒加载机制——将不同路由对应的组件分为不同代码块,当路由被访问时才对该组件进行渲染和加载。通过箭头函数导入的组件属于懒加载模式

(2) 路由器实例化并传入路由表

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

export default router

(3) 将路由器挂载到根实例

createApp(App).use(router).mount('#app')

注意:

将路由器挂载到Web应用后会产生全局路由器实例router和局部路由实例route,通过this.$router和this.$route的语法访问。

全局路由器对象常用的属性和方法如表6.3所示,局部路由对象常用的属性和方法如表6.4所示。

属性/方法含义
push()URL跳转(向浏览器history对象入栈记录)
go()以当前路由为基准,在history栈中前进或后退若干路由
replace()URL替换(不向浏览器history对象入栈记录)
routes全局路由映射表
currentRoute当前激活的路由
表6.3
属性/方法含义
path当前路由的绝对路径
params当前路由中动态片段和全匹配片段的键值对
query当前路由中查询参数的键值对
表6.4

(4) 路由渲染

<router-link to="/home" replace>Home</router-link>
<router-link to="/about" replace>About</router-link>
<!-- 动态路由 -->
<router-link :to="{path:'/user/'+ userId}" replace>User</router-link>

注意:
<router-link>默认会被渲染成<a>标签;<router-view>可视为组件占位符,根据当前路由在该位置动态渲染出不同的组件,而保持其他内容不变。

6.4 Vuex

Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用所有组件的状态,并以一定的规则保证状态以一种可预测的方式发生变化,提高可维护性。Vuex适用于存储多个组件共享、依赖的状态,或多个组件的行为都会变更的状态,此时全局管理可以避免繁琐的组件通信。

在这里插入图片描述

图6.4
核心概念解释
state单一状态树,用于存储全局状态。可通过this.$store.state访问。可通过Vue.set(obj, key, val)与Vue.delete(obj, key)来响应式地添加或删除state中的属性
mutations用于定义对状态改变的同步操作行为,便于Vuex跟踪状态变化。通过this.$store.commit()来修改状态
actions用于定义对状态改变的异步操作行为,便于Vuex跟踪状态变化。注意actions事件内部通过context.commit()修改状态,外部通过this.$store.dispatch()来触发异步行为
modules用于将复杂应用的大量状态模块化管理,每个模块拥有自己的state、mutations等属性。通过this.$store.state.module访问模块module中的属性

注意:

(1) 一般采用对象风格向Vuex进行提交状态改变,mutations中定义的方法可使用载荷payload来获取提交时额外传入的参数,例如:

// mutation-types.js
export const INCREMENT = "increment"

// index.js
// 导入事件常量
import {INCREMENT} from './mutation-types'
// Vuex配置
state: {
  count: 1
},
mutations: {
  [INCREMENT](state, payload) {
    state.count = payload.amount;
  }
},
// 提交状态变化
this.$store.commit({
  type: INCREMENT,
  amount: 10
})

推荐使用事件常量替代mutations事件类型,这样可以使linter之类的工具发挥作用,同时把这些常量放在单独的文件中便于维护整个应用包含的mutations事件。

(2) actions使用实例

actions: {
  [INFOUPDATE](context, payload) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        context.commit({
          type: INCREMENT,
          amount: payload.num
        });
        resolve('完成了');
      }, 1000)
    })
  }
},
// 建议使用Promise对异步操作的结果进行处理
this.$store.dispatch({
  type: INFOUPDATE,
  num: 10
}).then(res => {
  console.log(res)
})

(3) modules使用实例

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

this.$store.state.a // -> moduleA 的状态
this.$store.state.b // -> moduleB 的状态

(4) Vuex代码结构

└── store
    ├── index.js           # 导出store的文件
    ├── actions.js         # 根级别的 action
├── mutations.js       # 根级别的 mutation
├── mutation-types.js # mutations事件常量
├── action-types.js   # actions事件常量
    └── modules		  # 模块
        ├── cart.js        # 购物车模块
        └── products.js    # 产品模块

标签:教程,Vue,渲染,DOM,入门级,webpack,组件,路由
来源: https://blog.csdn.net/FRIGIDWINTER/article/details/120807844

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有