admin 管理员组

文章数量: 887017

文章目录

  • 1. 前后台分离开发
    • 1.1 前后台分离开发介绍
    • 1.2 YAPI
      • 1.2.1 YAPI介绍
      • 1.2.2 接口文档管理
  • 2. 前端工程化
    • 2.1 前端工程化介绍
    • 2.2 前端工程化入门
      • 2.2.1 环境准备
        • NodeJS安装
      • 2.2.2 Vue项目简介
        • 2.2.2.1 创建vue项目
        • 2.2.2.2 vue项目目录结构介绍
        • 2.2.2.3 调试VS Code中NPM脚本窗口
        • 2.2.2.4 运行vue项目
          • 第一种方式:
          • 第二种方式:
      • 2.2.3 Vue项目开发流程
  • 3. Vue组件库Element
    • 3.1 Element介绍
    • 3.2 快速入门
    • 3.3 Element组件
      • 3.3.1 Table表格
        • 3.3.1.1 组件演示
        • 3.3.1.2 组件属性详解
      • 3.3.2 Pagination分页
        • 3.3.2.1 组件演示
        • 3.3.2.2 组件属性详解
        • 3.3.2.3 组件事件详解
      • 3.3.3 Dialog对话框
        • 3.3.3.1 组件演示
        • 3.3.3.2 组件属性详解
      • 3.3.4 Form表单
        • 4.3.4.1 组件演示
    • 3.4 案例
  • 4. Vue路由
    • 4.1 路由介绍
    • 4.2 路由入门
  • 5. 打包部署
    • 5.1 前端工程打包
    • 5.2 部署前端工程
      • 5.2.1 nginx介绍
    • 5.2.2 部署
  • 6. 错误解决方案
            • Echarts 自适应

1. 前后台分离开发

1.1 前后台分离开发介绍

前端开发有2种方式:前后台混合开发前后台分离开发

前后台混合开发,顾名思义就是前台后台代码混在一起开发,如下图所示:

这种开发模式有如下缺点:

  • 沟通成本高:后台人员发现前端有问题,需要找前端人员修改,前端修改成功,再交给后台人员使用
  • 分工不明确:后台开发人员需要开发后台代码,也需要开发部分前端代码。很难培养专业人才
  • 不便管理:所有的代码都在一个工程中
  • 不便维护和扩展:前端代码更新,和后台无关,但是需要整个工程包括后台一起重新打包部署。

所以目前基本都是采用的前后台分离开发方式,如下图所示:

将原先的工程分为前端工程和后端工程这2个工程,然后前端工程交给专业的前端人员开发,后端工程交给专业的后端人员开发。前端页面需要数据,可以通过发送异步请求,从后台工程获取。前后台开发人员都需要遵循接口文档规范开发。接口文档的内容是根据产品经理提供的产品原型和需求文档所撰写出来的。

后台开发者开发一个功能的具体流程如下图所示:

  1. 需求分析:首先我们需要阅读需求文档,分析需求,理解需求。
  2. 接口定义:查询接口文档中关于需求的接口的定义,包括地址,参数,响应数据类型等等
  3. 前后台并行开发:各自按照接口文档进行开发,实现需求
  4. 测试:前后台开发完了,各自按照接口文档进行测试
  5. 前后段联调测试:前段工程请求后端工程,测试功能

1.2 YAPI

1.2.1 YAPI介绍

YAPI 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。其官网地址:http://yapi.smart-xwork/

YAPI 主要提供了2个功能:

  • API接口管理:根据需求撰写接口,包括接口的地址,参数,响应等等信息。
  • Mock服务:模拟真实接口,生成接口的模拟测试数据,用于前端的测试。

1.2.2 接口文档管理

  1. 登录YAPI的官网在个人空间中,选择项目列表->添加测试项目,效果如图所示:

  2. 然后点击创建的项目,进入到项目中,紧接着先添加接口的分类,如下图所示

  3. 选择当前创建的分类,创建接口信息,如下图所示:

  4. 紧接着来到接口的编辑界面,对接口做生层次的定制,例如:接口的参数,接口的返回值等等,效果图下图所示:

  5. 添加接口的请求参数,如下图所示:

  6. 添加接口的返回值,如下图所示:

  7. 保存上述设置,来到接口的预览界面,查询接口的信息,其效果如下图所示:篇幅有限,只截取部分

  8. 最后设置接口的mock信息

  9. 来到接口的Mock设置窗口,如下图所示:

  10. 来到接口的预览界面,直接点击Mock地址,如下图所示:

  11. 浏览器直接打开,并返回如下数据:

2. 前端工程化

2.1 前端工程化介绍

目前的前端开发中,当需要使用一些资源时,例如:vue.js,和axios.js文件,都是直接再工程中导入的,如下图所示:

上述开发模式存在如下问题:

  • 每次开发都是从零开始,比较麻烦
  • 多个页面中的组件共用性不好
  • js、图片等资源没有规范化的存储目录,没有统一的标准,不方便维护

现在企业开发中更加讲究前端工程化方式的开发,主要包括如下4个特点

  • 模块化:将js和css等,做成一个个可复用模块
  • 组件化:将UI组件,css样式,js行为封装成一个个的组件,便于管理
  • 规范化:提供一套标准的规范的目录接口和编码规范,所有开发人员遵循这套规范
  • 自动化:项目的构建,测试,部署全部都是自动完成

对于前端工程化,说白了,就是在企业级的前端项目开发中,把前端开发所需要的工具、技术、流程、经验进行规范化和标准化。从而提升开发效率,降低开发难度等等。

2.2 前端工程化入门

2.2.1 环境准备

前端工程化是通过vue官方提供的脚手架Vue-cli来完成的,用于快速的生成一个Vue的项目模板。Vue-cli主要提供了如下功能:

  • 统一的目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

需要运行Vue-cli,需要依赖NodeJS,NodeJS是前端工程化依赖的环境。

NodeJS安装
  1. 双击nodejs的安装包

  2. 选择安装目录【选择安装到一个,没有中文,没有空格的目录下(新建一个文件夹NodeJS)】

  3. 验证NodeJS环境变量【NodeJS 安装完毕后,会自动配置好环境变量,我们验证一下是否安装成功,通过: node -v】

  4. 配置npm的全局安装路径

    使用管理员身份运行命令行,在命令行中,执行如下指令:

    npm config set prefix "E:\develop\NodeJS"
    

​ 注意:E:\develop\NodeJS 这个目录是NodeJS的安装目录

  1. 切换npm的淘宝镜像

    使用管理员身份运行命令行,在命令行中,执行如下指令:

    npm config set registry https://registry.npm.taobao
    
  2. 安装Vue-cli

    使用管理员身份运行命令行,在命令行中,执行如下指令:

    npm install -g @vue/cli
    

    这个过程中,会联网下载,可能会耗时几分钟,耐心等待。

2.2.2 Vue项目简介

Vue-cli提供了如下2种方式创建vue项目:

  • 命令行:直接通过命令行方式创建vue项目

    vue create vue-project01
    
  • 图形化界面:通过命令先进入到图形化界面,然后再进行vue工程的创建

    vue ui
    

    图形化界面如下:

2.2.2.1 创建vue项目
  1. 首先,在桌面创建vue文件夹,然后双击进入文件夹,来到地址目录,输入cmd,然后进入到vue文件夹的cmd窗口界面,如下图所示:

  2. 当前目录下,直接输入命令vue ui进入到vue的图形化界面,如下图所示:

  3. 选择创建按钮,在vue文件夹下创建项目,如下图所示:

  4. 来到如下界面,进行vue项目的创建

  5. 预设模板选择手动,如下图所示:

  6. 功能页面开启路由功能,如下图所示:

  7. 配置页面选择语言版本和语法检查规范,如下图所示:

  8. 然后创建项目,进入如下界面:

  9. 只需要等待片刻,即可进入到创建创建成功的界面,如下图所示:

2.2.2.2 vue项目目录结构介绍

通过VS Code打开之前创建的vue文件夹,打开之后,呈现如下图所示页面:

vue项目的标准目录结构以及目录对应的解释如下图所示:

其中我们平时开发代码就是在src目录

2.2.2.3 调试VS Code中NPM脚本窗口
  1. 通过设置/用户设置/扩展/NPM更改NPM默认配置,如下图所示

  2. 重启VS Code,并且双击打开package.json文件,然后点击资源管理器处的3个小点勾选npm脚本选项,如图所示

然后就能显示NPM脚本小窗口了。

2.2.2.4 运行vue项目
  • 第一种方式:
  • 通过VS Code提供的图形化界面 ,如下图所示:(注意:NPM脚本窗口默认不显示,可以参考本节的最后调试出来)

  1. 点击之后,等待片刻,即可运行,在终端界面中,我们发现项目是运行在本地服务的8080端口,我们直接通过浏览器打开地址.

  2. 浏览器打开后,呈现如下界面,表示项目运行成功

  3. 此时访问的是 src/App.vue这个根组件,此时我们打开这个组件,修改代码:添加内容Vue

  4. 保存更新的代码,直接打开浏览器,不需要做任何刷新,发现页面呈现内容发生了变化,如下图所示:

  5. vue项目的热更新功能对于8080端口,经常被占用,所以可以去修改默认的8080端口。修改vue.config.js文件的内容,添加如下代码:

devServer:{
    port:7000
}
  1. 如下图所示,关闭服务器,并且重新启动:

  2. 重新启动如下图所示:

  3. 端口更改成功,可以通过浏览器访问7000端口来访问我们之前的项目

  • 第二种方式:
  • 命令行方式

  1. 直接基于cmd命令窗口,在vue目录下,执行输入命令npm run serve即可,如下图所示:

2.2.3 Vue项目开发流程

访问的首页是index.html,但是找到public/index.html文件,打开之后发现,里面没有什么代码,index.html的代码很简洁,但是浏览器所呈现的index.html内容却很丰富,代码和内容不匹配,对于vue项目,index.html文件默认是引入了入口函数main.js文件

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
router,
render: h => h(App)
}).$mount('#app')

上述代码中,包括如下几个关键点:

  • import: 导入指定文件,并且重新起名。例如上述代码import App from './App.vue'导入当前目录下得App.vue并且起名为App
  • new Vue(): 创建vue对象
  • $mount(‘#app’);将vue对象创建的dom对象挂在到id=app的这个标签区域中,作用和之前学习的vue对象的le属性一致。
  • router: 路由,详细在后面的小节讲解
  • render: 主要使用视图的渲染的。

main.js中通过代码挂载到index.html的id=app的标签区域的。vue创建的dom对象挂载到id=app的标签区域,还是没有解决最开始的问题:首页内容如何呈现的?这就涉及到render中的App了,如下图所示:

那么这个App对象怎么回事呢,打开App.vue,注意的是.vue结尾的都是vue组件。而vue的组件文件包含3个部分():

  • template: 模板部分,主要是HTML代码,用来展示页面主体结构的
  • script: js代码区域,主要是通过js代码来控制模板的数据来源和行为的
  • style: css样式部分,主要通过css样式控制模板的页面效果得

如下图所示就是一个vue组件的小案例:

App.vue组件的template部分内容,和我们浏览器访问的首页内容是一致的,如下图所示:

添加script部分的数据模型,删除css样式,完整代码如下:

<template>
  <div id="app">
    {{message}}
  </div>
</template>

<script>
export default {
  data(){
    return {
      "message":"hello world"
    }
  }
}
</script>
<style>

</style>

保存直接,回到浏览器,发现首页展示效果发生了变化,如下图所示:

3. Vue组件库Element

3.1 Element介绍

前端开发模式MVVM,vue是侧重于VM开发的,主要用于数据绑定到视图的,ElementUI就是一款侧重于V开发的前端框架,主要用于开发美观的页面的。

Element:是饿了么公司前端开发团队提供的一套基于 Vue 的网站组件库,用于快速构建网页。

Element 提供了很多组件(组成网页的部件)供我们使用。例如 超链接、按钮、图片、表格等等。如下图所示就是我们开发的页面和ElementUI提供的效果对比:可以发现ElementUI提供的各式各样好看的按钮

其官网地址:https://element.eleme/#/zh-CN

3.2 快速入门

  1. 安装ElementUI的组件库:
npm install element-ui@2.15.3 

​ 具体操作如下图所示:

  1. 在main.js这个入口js文件中引入ElementUI的组件库,其代码如下:
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

​ 具体操作如图所示:

  1. 去ElementUI的官网,找到组件库相关组件代码复制到我们的ElementView.vue组件文件中

  2. 在默认访问的根组件src/App.vue中引入我们自定义的组件,具体操作步骤如下:
    运行vue项目,浏览器直接访问之前的7000端口,展示效果如下图所示:

3.3 Element组件

3.3.1 Table表格

3.3.1.1 组件演示

Table 表格:用于展示多条结构类似的数据,可对数据进行排序、筛选、对比或其他自定义操作。到ElementUI的组件库中,找到表格组件

复制代码到之前的ElementVue.vue组件中,需要注意的是,组件包括了3个部分,如果官方有除了template部分之外的style和script都需要复制。

ElementView.vue组件文件整体代码如下:

<template>
 <div>
 <!-- Button按钮 -->
     <el-row>
         <el-button>默认按钮</el-button>
         <el-button type="primary">主要按钮</el-button>
         <el-button type="success">成功按钮</el-button>
         <el-button type="info">信息按钮</el-button>
         <el-button type="warning">警告按钮</el-button>
         <el-button type="danger">危险按钮</el-button>
     </el-row>

     <!-- Table表格 -->
     <el-table
     :data="tableData"
     style="width: 100%">
         <el-table-column
             prop="date"
             label="日期"
             width="180">
         </el-table-column>
         <el-table-column
             prop="name"
             label="姓名"
             width="180">
         </el-table-column>
         <el-table-column
             prop="address"
             label="地址">
         </el-table-column>
     </el-table>
 </div>
</template>

<script>
export default {
  data() {
     return {
       tableData: [{
         date: '2016-05-02',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1518 弄'
       }, {
         date: '2016-05-04',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1517 弄'
       }, {
         date: '2016-05-01',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1519 弄'
       }, {
         date: '2016-05-03',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1516 弄'
       }]
     }
   }
}
</script>

<style>

</style>

此时回到浏览器页面呈现如下效果:

3.3.1.2 组件属性详解

ElementUI将数据模型绑定到视图主要通过如下几个属性:

  • data: 主要定义table组件的数据模型
  • prop: 定义列的数据应该绑定data中定义的具体的数据模型
  • label: 定义列的标题
  • width: 定义列的宽度

其具体示例含义如下图所示:

PS:Element组件的所有属性都可以在官网组件页面的最下方找到,如下图所示:

3.3.2 Pagination分页

3.3.2.1 组件演示

Pagination: 分页组件,主要提供分页工具条相关功能。

复制代码到ElementView.vue组件文件的template中,拷贝如下代码:

<el-pagination
 background
 layout="prev, pager, next"
 :total="1000">
</el-pagination>

浏览器打开呈现如下效果:

3.3.2.2 组件属性详解

对于分页组件需要关注的是如下几个重要属性(可以通过查阅官网组件中最下面的组件属性详细说明得到):

  • background: 添加北京颜色,也就是上图蓝色背景色效果。
  • layout: 分页工具条的布局,其具体值包含sizes, prev, pager, next, jumper, ->, total, slot 这些值
  • total: 数据的总数量

根据官方分页组件提供的layout属性说明,如下图所示:

修改layout属性如下:

 layout="sizes,prev, pager, next,jumper,total"

浏览器打开呈现如下效果:

在原来的功能上,添加了一些额外的功能,其具体对应关系如下图所示:

3.3.2.3 组件事件详解

对于分页组件,除了上述几个属性,还有2个非常重要的事件:

  • size-change : pageSize 改变时会触发
  • current-change :currentPage 改变时会触发

其官方详细解释含义如下图所示:

对于这2个事件的参考代码,可以通过官方提供的完整案例中找到,如下图所示:

首先复制事件,复制代码如下:

@size-change="handleSizeChange"
@current-change="handleCurrentChange"

Pagination组件的template完整代码如下:

<!-- Pagination分页 -->
<el-pagination
               @size-change="handleSizeChange"
               @current-change="handleCurrentChange"
               background
               layout="sizes,prev, pager, next,jumper,total"
               :total="1000">
</el-pagination>

紧接着需要复制事件需要的2个函数,需要注意methods属性和data同级,其代码如下:

methods: {
      handleSizeChange(val) {
        console.log(`每页 ${val}`);
      },
      handleCurrentChange(val) {
        console.log(`当前页: ${val}`);
      }
    },

此时Pagination组件的script部分完整代码如下:

<script>
export default {
    methods: {
      handleSizeChange(val) {
        console.log(`每页 ${val}`);
      },
      handleCurrentChange(val) {
        console.log(`当前页: ${val}`);
      }
    },
     data() {
        return {
          tableData: [{
            date: '2016-05-02',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1518 弄'
          }, {
            date: '2016-05-04',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1517 弄'
          }, {
            date: '2016-05-01',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1519 弄'
          }, {
            date: '2016-05-03',
            name: '王小虎',
            address: '上海市普陀区金沙江路 1516 弄'
          }]
        }
      }
}
</script>

回到浏览器中,我们f12打开开发者控制台,然后切换当前页码和切换每页显示的数量,呈现如下效果:

3.3.3 Dialog对话框

3.3.3.1 组件演示

Dialog: 在保留当前页面状态的情况下,告知用户并承载相关操作。其企业开发应用场景示例如下图所示:

在ElementUI官方找到Dialog组件,复制如下代码到我们的组件文件的template模块中

<br><br>
<!--Dialog 对话框 -->
<!-- Table -->
<el-button type="text" @click="dialogTableVisible = true">打开嵌套表格的 Dialog</el-button>

<el-dialog title="收货地址" :visible.sync="dialogTableVisible">
 <el-table :data="gridData">
     <el-table-column property="date" label="日期" width="150"></el-table-column>
     <el-table-column property="name" label="姓名" width="200"></el-table-column>
     <el-table-column property="address" label="地址"></el-table-column>
 </el-table>
</el-dialog>

并且复制数据模型到script模块中:

gridData: [{
       date: '2016-05-02',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }, {
       date: '2016-05-04',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }, {
       date: '2016-05-01',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }, {
       date: '2016-05-03',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }],
     dialogTableVisible: false,

其完整的script部分代码如下:

<script>
export default {
 methods: {
   handleSizeChange(val) {
     console.log(`每页 ${val}`);
   },
   handleCurrentChange(val) {
     console.log(`当前页: ${val}`);
   }
 },
  data() {
     return {
     gridData: [{
       date: '2016-05-02',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }, {
       date: '2016-05-04',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }, {
       date: '2016-05-01',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }, {
       date: '2016-05-03',
       name: '王小虎',
       address: '上海市普陀区金沙江路 1518 弄'
     }],
     dialogTableVisible: false,
     tableData: [{
         date: '2016-05-02',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1518 弄'
       }, {
         date: '2016-05-04',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1517 弄'
       }, {
         date: '2016-05-01',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1519 弄'
       }, {
         date: '2016-05-03',
         name: '王小虎',
         address: '上海市普陀区金沙江路 1516 弄'
       }]
     }
   }
}
</script>

打开浏览器,点击按钮,呈现如下效果:

3.3.3.2 组件属性详解

ElementUI对话框的显示与隐藏通过如下的属性实现:

  • visible.sync :是否显示 Dialog

visible属性绑定的dialogTableVisble属性一开始默认是false,所以对话框隐藏;通过点击按钮,触发事件,修改属性值为true,然后对话框visible属性值为true,所以对话框呈现出来。

3.3.4 Form表单

4.3.4.1 组件演示

Form 表单:由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据。

<template>
 <div>
     </el-dialog>
     <!-- 对话框-表单 -->
     &nbsp;&nbsp;&nbsp;
     <el-button type="text" @click="dialogFormVisible = true">打开嵌套表单的 Dialog</el-button>

     <el-dialog title="收货地址" :visible.sync="dialogFormVisible">
         <el-form :model="form">

             <el-form-item label="活动名称" :label-width="formLabelWidth">
                 <el-input v-model="form.name" autocomplete="off"></el-input>
             </el-form-item>

             <el-form-item label="活动区域" :label-width="formLabelWidth">
                 <el-select v-model="form.region" placeholder="请选择活动区域">
                     <el-option label="上海" value="shanghai"></el-option>
                     <el-option label="北京" value="beijing"></el-option>
                 </el-select>
             </el-form-item>

         </el-form>

         <div slot="footer" class="dialog-footer">
             <el-button @click="dialogFormVisible = false">取 消</el-button>
             <el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
         </div>
     </el-dialog>
 </div>
</template>
<script>
export default {
 //数据区
 data () {
     return {
         //对话框是否显示
         dialogTableVisible:false,
         dialogFormVisible:false,
         //表单双向绑定数据
         form:{
             name:"张三",
             region:"重庆"
         },
         //宽度
         formLabelWidth:"120px",

 },
 //方法区
 methods: {

 },
}
</script>

3.4 案例

需求说明:

  1. 制作类似格式的页面

    即上面是标题,左侧栏是导航,右侧是数据展示区域

  2. 右侧需要展示搜索表单

  3. 右侧表格数据是动态展示的,数据来自于后台

//ElementView.vue
//模板
<template>
    <div>
        <!-- 容器 -->
        <el-container style="height: 700px;border: 1px solid #eee;">
          <el-header style="font-size: 40px;background-color: rgb(238, 241, 246);">
            美国有苹果 中国有菠萝 🍍手机
          </el-header>
          <el-container>
            <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
                <!-- 导航栏 -->
                <el-menu :default-openeds="['1', '3']">
                    <el-submenu index="1">
                    <template slot="title"><i class="el-icon-message"></i>系统信息管理</template>
                        <!-- <template slot="title"></template> -->
                        <el-menu-item index="1-1">
                          <router-link to="/dept">部门管理</router-link>
                        </el-menu-item>
                        <el-menu-item index="1-2">
                          <router-link to="/emp">员工管理</router-link>
                        </el-menu-item>
                        <el-menu-item index="1-3">
                          <router-link to="/element">Element测试</router-link>
                        </el-menu-item>
                    </el-submenu>
                </el-menu>
            </el-aside>
            <el-main>
                <!-- 行内表单 -->
                <el-form :inline="true" :model="SeacherForm" class="demo-form-inline">
                  <el-form-item label="姓名">
                    <el-input v-model="SeacherForm.name" placeholder="姓名"></el-input>
                  </el-form-item>
                  <el-form-item label="性别">
                    <el-select v-model="SeacherForm.gender" placeholder="性别">
                      <el-option label="男" value="1"></el-option>
                      <el-option label="女" value="2"></el-option>
                    </el-select>
                  </el-form-item>
                  <el-form-item label="入职日期">
                    <el-date-picker
                      v-model="SeacherForm.entryDate"
                      type="datetimerange"
                      :picker-options="pickerOptions"
                      range-separator="至"
                      start-placeholder="开始日期"
                      end-placeholder="结束日期"
                      align="right">
                    </el-date-picker>
                  </el-form-item>
                  <el-form-item>
                    <el-button type="primary" @click="seacher">查询</el-button>
                  </el-form-item>
                </el-form>

                <!-- 数据栏表格 -->
                <el-table :data="tableData" style="width: 100%">
                  <el-table-column prop="name" label="姓名" width="180"> </el-table-column>
                  <el-table-column prop="image" label="图像" width="180">
                    <template slot-scope="scope">
                      <img :src="scope.row.image" width="60px" height="80px"/>
                    </template>
                  </el-table-column>
                  <el-table-column prop="gender" label="性别" width="180">
                    <template slot-scope="scope">
                     {{scope.row.gender==1?"男":"女"}}
                    </template>
                  </el-table-column>
                  <el-table-column prop="job" label="职位" width="180"> </el-table-column>
                  <el-table-column prop="entrydate" label="入职日期" width="180"> </el-table-column>
                  <el-table-column prop="updatetime" label="最后操作时间"> </el-table-column>
                  <!-- 按钮 -->
                  <el-table-column width="80" >
                  <el-button type="primary" size="mini">编辑</el-button>
                  </el-table-column>
                  <el-table-column width="80" style="margin-left: 5px;">
                    <el-button type="danger" size="mini">删除</el-button>
                  </el-table-column>
                </el-table>

                <!-- 分页工具栏 -->
                <el-pagination
                    background
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="pagination.currentPage"
                    :page-sizes="[10, 20, 50, 100]"
                    :page-size="pagination.pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="pagination.total">
                </el-pagination>

            </el-main>
          </el-container>
        </el-container>
        
    </div>
</template>
//脚本
<script>
import axios from 'axios';
export default {
    //数据区
    data() {
        return {
            tableData:[],
            SeacherForm:{
                name:"",
                gender:"",
                entryDate:[]
            },
            //当前时间日期选择器特有的选项参考下表
            pickerOptions:{},
            //分页数据
            pagination:{
                //总条数
                total:200,
                //每页显示条数
                pageSize:10,
                //当前页码
                currentPage:1
            },
        }
    },
    //方法区
    methods: {
        seacher(){
          console.log(this.SeacherForm);
        },
        //currentPage改变时会触发的函数
        handleCurrentChange(currentPage){
            console.log("页码发生了变化,最新页码为:" + currentPage);
        },
        //pageSize改变时会触发的函数
        handleSizeChange(pageSize){
            console.log("每页条数发生了变化,最新每页条数为:" + pageSize);
        }
    },
    //钩子函数
    mounted(){
        axios.get("http://yapi.smart-xwork/mock/169327/emp/list")
        .then(res => {
          if(res.data.code){
            this.tableData = res.data.data;
          }else{
            alert(res.data.message);
          }
        })
        .catch(err => {
            console.error(err); 
        })
    }
}
</script>
//样式
<style>

</style>
<!-- 模板(定义HTML标签) -->
<template>
  <div id="app">
    <router-view></router-view>    
  </div>
</template>

//脚本  vue脚手架组件
<script>
export default {
  components: {  },
  //数据区
  data () {
    return {
      message:"hello vue-cli"
    }
  },
  //方法区
  methods: {
    
  },
  //钩子函数
  mounted() {
    
  },
}
</script>

<!-- 样式 -->
<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>
//  路径:项目下/src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
// import HomeView from '../views/HomeView.vue'

Vue.use(VueRouter)

const routes = [
  {
    path:'/',
    redirect: '/emp',
    // name:'emp',
    // component: () => import('../views/polo/EmpView.vue')
  },
  {
    path: '/emp',
    name: 'emp',
    component: () => import('../views/polo/EmpView.vue')
  },
  {
    path: '/dept',
    name: 'dept',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import('../views/polo/DeptView.vue')
  },
  {
    path: '/element',
    name: 'element',
    component: () => import('../views/element/ElementView.vue')
  }
]

const router = new VueRouter({
  routes
})

export default router
// src/main.js
import Vue from 'vue'
import ElementUI,{Table} from 'element-ui';
// import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'
import router from './router'

// 解决 ElTable 自动宽度高度导致的「ResizeObserver loop limit exceeded」问题
const fixElTableErr = (table) => {
  const oldResizeListener = table.methods.resizeListener;
  table.methods.resizeListener = function () {
      window.requestAnimationFrame(oldResizeListener.bind(this));
  };
};
// 一定要在Vue.use之前执行此函数
fixElTableErr(Table);

Vue.use(ElementUI);
Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

4. Vue路由

4.1 路由介绍

将资代码/vue-project(路由)/vue-project/src/views/polo/DeptView.vue拷贝到我们当前EmpView.vue同级,其结构如下:

前端路由:URL中的hash(#号之后的内容)与组件之间的对应关系

点击左侧导航栏时,浏览器的地址栏会发生变化,路由自动更新显示与url所对应的vue组件。vue官方提供了路由插件Vue Router,其主要组成如下:

  • VueRouter:路由器类,根据路由请求在路由视图中动态渲染选中的组件
  • <router-link>:请求链接组件,浏览器会解析成<a>
  • <router-view>:动态视图组件,用来渲染展示与路由路径对应的组件

其工作原理如下图所示:

  1. 首先VueRouter根据我们配置的url的hash片段和路由的组件关系去维护一张路由表;

  2. 然后页面提供一个<router-link>组件,用户点击,发出路由请求;

  3. 接着VueRouter根据路由请求,在路由表中找到对应的vue组件;

  4. 最后VueRouter会切换<router-view>中的组件,从而进行视图的更新

4.2 路由入门

需要先安装vue-router插件命令如下:

npm install vue-router@3.5.1

如果前面通过命令行安装vue-cli的需要执行上述指令,图形界面,已经安装了router。

然后需要在src/router/index.js文件中定义路由表,根据其提供的模板代码进行修改

//  路径:项目下/src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
// import HomeView from '../views/HomeView.vue'

Vue.use(VueRouter)

const routes = [
  {
    path:'/',
    redirect: '/emp',
    // name:'emp',
    // component: () => import('../views/polo/EmpView.vue')
  },
  {
    path: '/emp',
    name: 'emp',
    component: () => import('../views/polo/EmpView.vue')
  },
  {
    path: '/dept',
    name: 'dept',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import('../views/polo/DeptView.vue')
  },
  {
    path: '/element',
    name: 'element',
    component: () => import('../views/element/ElementView.vue')
  }
]

const router = new VueRouter({
  routes
})

export default router

注意需要去掉没有引用的import模块。

路由基本信息配置好了,路由表已经被加载,此时还缺少2个东西,就是<router-lin>和<router-view>,所以需要修改2个页面(EmpView.vue和DeptView.vue)左侧栏的2个按钮为router-link,其代码如下:

<el-menu-item index="1-1">
    <router-link to="/dept">部门管理</router-link>
</el-menu-item>
<el-menu-item index="1-2">
    <router-link to="/emp">员工管理</router-link>
</el-menu-item>

然后还需要在内容展示区域即App.vue中定义route-view,作为组件的切换,其App.vue的完整代码如下:

<template>
  <div id="app">
    <!-- {{message}} -->
    <!-- <element-view></element-view> -->
    <!-- <emp-view></emp-view> -->
    <router-view></router-view>
  </div>
</template>

<script>
// import EmpView  './views/polo/EmpView.vue'
// import ElementView  './views/element/ElementView.vue'
export default {
  components: { },
  data(){
    return {
      "message":"hello world"
    }
  }
}
</script>
<style>

</style>

但是我们浏览器打开地址: http://localhost:7000/ ,发现一片空白,因为我们默认的路由路径是/,但是路由配置中没有对应的关系,

所以我们需要在路由配置中/对应的路由组件,代码如下:

const routes = [
  {
    path: '/emp',
    name: 'emp',
    component:  () => import('../views/polo/EmpView.vue')
  },
  {
    path: '/dept',
    name: 'dept',
    component: () => import('../views/polo/DeptView.vue')
  },
  {
    path: '/',
    redirect:'/emp' //表示重定向到/emp即可
  },
]

打开浏览器,访问http://localhost:7000 发现直接访问的是emp的页面,并且能够进行切换如上述案例所示

5. 打包部署

前端工程开发好后,发布需要2步:

  1. 前端工程打包
  2. 通过nginx服务器发布前端工程6.2 部署前端工程

5.1 前端工程打包

直接通过VS Code的NPM脚本中提供的build按钮来完整,如下图所示,直接点击即可:

然后会在工程目录下生成一个dist目录,用于存放需要发布的前端资源,如下图所示:

5.2 部署前端工程

5.2.1 nginx介绍

nginx: Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强,在各大型互联网公司都有非常广泛的使用。niginx在windows中的安装是比较方便的,直接解压即可。下图所示就是nginx的解压目录以及目录结构说明:

如果要发布,直接将资源放入到html目录中即可。

5.2.2 部署

  1. 将之前打包的前端工程dist目录下得内容拷贝到nginx的html目录下

  2. 通过双击nginx下得nginx.exe文件来启动nginx

  3. nginx服务器的端口号是80,故在浏览器直接访问http://localhost:80

  4. PS: 如果80端口被占用,可以通过conf/nginx.conf配置文件来修改端口号

6. 错误解决方案

element-ui低版本报 ResizeObserver loop limit exceeded 这个错误的原因:

  1. 如果在一个动画帧内,ResizeObserver不能处理所有的observations,就会触发这个ResizeObserver loop limit exceeded

  2. 监听一个元素大小变化或者屏幕横竖屏时,需要监听window.resize事件或者window.orientationchange方法。由于 resize 事件会在一秒内触发将近60次,所以很容易在改变窗口大小时导致性能问题,所以 window.resize 事件通常是浪费的,不过我们有节流可以有效提高性能。 可是 window.resize 只能监听 window 对象,如果对于一个 div ,我们是无能为力的,还好有 resize-observer-polyfill,包里面有用到 MutationObserver API 有兴趣可以看看 可以监听到元素的变动。

    所以想要 table 自适应,需要父级的宽度有触发到变化,addResizeListener 事件自动会触发,自适应自然就解决了。

解决办法:

转载至

  1. 父元素设置 position: relative ,table 外层新增一个 div, 设置 position: absolute; 这样 table 就会跟随父元素的宽度变化了。结构大概是,(实测只需table 外层新增一个 div 设置 position: absolute; )
<div style="position: relative;">
    <div style="position: absolute;">  
        <el-table/>
    </div>
</div>
  1. 既然这样我为何不在图二右内容的宽度 在 resize 是设置宽度,都是 resize ,设置多一个宽度 不会造成什么浪费。这样就能强制其子元素宽度发生变化,table 也就理所当然的改变宽度了。 所以 table 的问题解决(未测试)

    Echarts 自适应

    echarts 提供了 resize 的 api,不过需要自己去调用。方法很简单。

    data: () {
      return {
        chart: null //初始化之后赋值为图表
      }
    },
    mounted() {
      addListener(document.getElementById(&#39;analyze&#39;), this.chartResize)
    },
    beforeDestroy() {
      removeListener(document.getElementById(&#39;analyze&#39;), this.chartResize)
    },
    methods: {
        chartResize() {
          setTimeout(() =&gt; {
            this.chart &amp;&amp; this.chart.resize()
          })
        },  
    }
    

转载至

  1. resize时,给回调进行节流,使其1帧内最多执行一次。代码如下:

    //main.js
    import Vue from 'vue';
    import ElementUI, { Table } from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    import App from './App.vue';
    // 解决 ElTable 自动宽度高度导致的「ResizeObserver loop limit exceeded」问题
    const fixElTableErr = (table) => {
        const oldResizeListener = table.methods.resizeListener;
        table.methods.resizeListener = function () {
            window.requestAnimationFrame(oldResizeListener.bind(this));
        };
    };
    // 一定要在Vue.use之前执行此函数
    fixElTableErr(Table);
    
    Vue.use(ElementUI);
    
    new Vue({
        el: '#app',
        render: (h) => h(App),
    });
    
  2. 升级element-ui至最新版本(最新版已解决此问题)

npm install element-ui@2.15.13

本文标签: 路由 组件 后端 日记 工程