前端与移动开发----Vue----vue配合webpack工程化实践

Vue配合webpack工程化实践

回顾webpack

能够说出什么是前端工程化 能够说出 webpack 的作用 能够掌握 webpack 的基本使用 了解常用 plugin 的基本使用 了解常用 loader 的基本使用 能够说出 Source Map 的作用
采用工程化的方式来开发前端项目。区别于传统开发,工程化开发会有源文件和目标的概念,在开发阶段写源文件,在发布阶段时,用webpack进行打包,发布。 模块打包器。对模块进行打包。 1. 先要有一个项目(npm init)。2. 本地安装webpack。3. 根据webpack配置文件要求,去写各类配置:mode, entry, output, devtool, plugin, loader… 最终完成打包功能。 1. 安装2.wepback.config.js中去配置plugins. 3.每一个plugin都有自己的操作细节,要单独去学习使用(查官网)。 3. 常用的有:(1)清空打包目录(2)压缩 (3)抽取css到独立文件 (4)复制index.html到目录文件夹 1. 作用:去帮助webpack去加载非.js模块。 一个文件类型对应一个Loader。2.使用步骤:(1)下载安装(2)配置module,rules 3.每个Loader的使用细节都不一样。4. 常用的:css-loader, style-loader, less-loader, postcss-loader, file-loader,babel-loader,url-loader 帮助我们去调试代码。因为源文件打包成目标代码之后(多个文件混成了一个),不方便调试代码。可以用souce-map来对源代码和目标文件之间生成映射关系

hero案例-项目介绍

综合应用: vue, 组件,路由,webpack

在这里插入图片描述

功能介绍:

  • 三个模块
    • 英雄列表(只做这个)
    • 装备列表
    • 技能列表
  • 英雄列表
    • 列表组件
    • 删除功能
    • 添加组件
    • 编辑组件

回顾为什么要讲webpack?

在这里插入图片描述

在这里插入图片描述

用webpack搭建开发环境

用webpack搭建一个支持单文件组件(一个组件就放在一个文件中)的开发环境。

目录结构

在这里插入图片描述

创建一个空文件夹,并做初始化

建立文件夹,名为heros,在文件夹下,做npm init --yes

在这里插入图片描述

安装包

安装需要用到的各种包。

### 开发依赖
npm i html-webpack-plugin vue-loader vue-style-loader vue-template-compiler [email protected] [email protected] webpack-dev-server -D

### 生产依赖
npm i vue
  • html-webpack-plugin: 1.把指定的模板拷贝到打包出口。2. 自动插入打包后的资源地址
  • vue-loader vue-style-loader vue-template-compiler : 用它们用来处理.vue文件(webpack是不支持.vue的,所以需要用loader)
  • [email protected] [email protected] : 要用webpack必须要先安装它们。这里指定版本的原因是:如果不指定,会默认安装最新版本,目前最新版与webpack-dev-server有些不兼容。
  • webpack-dev-server: 在开发阶段,在内存中实时打包,更新显示结果。
  • -D: (前面有一个空格)它表示开发依赖。
    • 生产依赖:房子卖出去了,现在已经住人了,就不需要挖掘机了。 线路,下水道还是需要的。(线路,下水道 就是生产依赖)
    • 开发依赖:在建房子的过程中,要用到挖掘机。建房子的过程是依赖于挖掘机。 (挖掘机就开发依赖);在建房子的过程中,需要线路,下水道…
  • npm i vue: 下载使用vue.js。与我们之前使用vue.js不同(我们之前是直接script src=“xx.vue.js”),现在有了webpack及nodejs项目,就可以用更高级的方式来使用。 不加-D:项目开发完成,这个包还是要用到的。

配置webpack.config.js

在根目录下,创建一个名为webpack.config.js的文件,内容如下:

  • 入口
  • 出口
  • devServer
  • .vue的loader
  • HtmlWebpackPlugin
const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    
    
  // 配置 webpack-dev-server的选项
  devServer: {
    
    
    host: '127.0.0.1',  // 配置启动ip地址
    port: 10088,  // 配置端口
    open: true  // 配置是否自动打开浏览器
  },
  output: {
    
    
    // 拼接一个绝对路由
    path: path.join(__dirname, './dist'),
    filename: 'bundle.js'
  },
  entry: './src/main.js', // 入口
  // 插件来于拓展webpack功能
  module: {
    
    
    rules: [
      {
    
    
        test: /\.vue$/,
        use: 'vue-loader'
      }
    ]
  },
  plugins:[
    new HtmlWebpackPlugin({
    
    
      minify: {
    
     // 压缩HTML文件
        removeComments: true, // 移除HTML中的注释
        collapseWhitespace: true, // 删除空白符与换行符
        minifyCSS: true// 压缩内联css
      },
      filename: 'index.html',
      // path.resolve()就是转成绝对路径
      template: path.resolve('./public/index.html') // 指定模板的位置
    }),
    new VueLoaderPlugin()
  ]
}

创建模板文件

在根目录下,创建public/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>
</head>

<body>
    <div id="app">

    </div>
</body>

</html>

在html-webpack-plugin工作时,把它拷贝到指定的出口文件夹下。

创建入口文件

在根目录下,创建: src/main.js 。内容如下:

// 入口文件
// import es6的模块化, 用来导入另一个模块
// 引入根组件
import app from './app.vue'
// console.log(app)

import Vue from 'vue'

new Vue({
    
    
  // 这是Vue的配置项,名是render,值是一个函数。
  // 功能: 把app这个组件手挂载到vue实例 ------ vue实例中就只一个根组件
  render: function (h) {
    
     return h(app) }
}).$mount('#app')


// #app: 就是public/index.html中的div

创建vue根组件

在src下面创建app.vue文件, src/app.vue 它的后缀名是.vue,需要在vscode中安装插件(vetur)才可以正确显示出来代码高亮效果。

内容如下:

<template>
  <div>
    这是一个vue的组件!!!!!
    </div>
</template>

下面是建议安装vscode拓展:

在这里插入图片描述

补充一个dev命令

在这里插入图片描述

启动项目

运行 npm run dev。 这里的dev就是上面在package.json中指定的属性名,当然,你也可以换成其它的名字。

在这里插入图片描述

验收成果

在这里插入图片描述

单文件组件(SFC)

single file component

用一个.vue为后缀的文件来声明一个组件,在webpack的加持下解析这个文件。

在这里插入图片描述

以前定义组件

// 全局注册组件
Vue.component('com-a',{
    
    
  // 组件配置对象
  template: '<div>组件结构</div>'
})

new Vue({
    
    
  // 局部注册组件
  components: {
    
    
    'com-b': {
    
    
      // 组件配置对象
      template: '<div>组件结构</div>'
    }
  }
})

无论是何种注册方式,每个组件必须有一个组件配置化对象。

现在定义组件

在vue-loader 配合webpack中

  • 以 .vue 文件的方式来定义组件,文件的内容代表的是:组件配置对象,称为:单文件组件。
  • 共有三个部分
    • template(必须 ): 约定这个组件的视图
    • script(可选): 设置除了template之外的组件配置项,并默认导出
    • style(可选 ): 用来组件中元素的样式(注意css-loader, style-loader的使用)
  • 需要渲染这个组件
    • 使用这个配置对象进行 全局注册 或者 局部注册,再来使用。
    • 使用路由规则中的 component 选项指定路由配置对象。

.vue文件的格式:

// 相对于组件配置对象中 template 选项,声明组件结构。
<template>
  <div class="red">必须有一个root(根)标签 {
   
   {msg}}</div>
</template>
// 如果有其他的配置选项,需要默认导出一个对象,在对象中声明其他配置选项
<script>
export default {
     
     
  // 其他组件配置对象
  data () {
     
     
    return {
     
     
      msg: '单文件组件数据'
    }
  }
}  
</script>
// 当前组件需要样式
<style>
  .red{
     
     
    color: red;
  }
</style>

用这种方式定义组件,结构 逻辑 样式 分工明确,非常清晰,便于维护。

  • style中scoped属性作用:限制样式在当前组件下生效。

单文组件必须要有配置的环境来支持,在浏览器直接使用是不行的。

ES6模块化

es6的模块 — 与vue无关

webpack与vue无关

不要认为 学习了es6的模块化,webpack只能在vue中使用!

什么是模块化

目标:让一个.js文件(A.js)中可以引用另一个.js文件(B.js)中的代码

模块: 就是一个文件。

模块化的规范:

  • commonjs 规范。nodejs中是commonjs规范。
  • es6规范。ECMAScript规范。

在nodejs中,模块化规范:CommonJS

  • 导出:module.exports = '导出的内容' exports.xxx = '导出的内容'
  • 导入:const path = require('path')

nodejs中的模块化与es6中的模块化是不同的。

ES6模块化

ES6也提供了模块化规范:ES6模块

  • 默认导入与导出
    • 导出:export default 导出内容
    • 导入:import 变量名 from 'js模块路径'
  • 按需导入与导出
    • 导出:
      • export const 成员变量名1 = '导出内容1'
      • export const 成员变量名2 = '导出内容2'
    • 导入:import {成员变量名1} from 'js模块路径'

基本步骤

(1) 先导出

(2) 再导入

定义模块: src/es6module/a.js

  • 创建一个.js文件,其中,写一些变量,函数…
// 创建一个模块

// 如何把es6module/a.js中的fn这个函数给main.js中去使用?

const a = 100
function fn(x,y) {
    
    
    return x + y
}

导出模块

// 创建一个模块

// 如何把es6module/a.js中的fn这个函数给main.js中去使用?

// const a = 100
function fn(x,y) {
    
    
    return x + y
}

// 导出模块中的fn
export {
    
     fn }

导入模块

main.js

import {
    
     fn } from './es6module/a.js'
console.log(fn(100,200)) // 300

默认导入与导出

module-a.js

// 模块a  默认导出
// 可以导出任何格式的内容:字符串  数字  函数  对象  数组 ...
export default {
    
    
  name: '模块A',
  attr: '属性'
}

main.js

import obj from './module-a'
console.log(obj) // {name: "模块A", attr: "属性"}

按需导入与导出

导出

module-b.js

// 模块B  按需导出
export function fn(x,y) {
    
    
    return x + y
}

export function fn1(x,y,z) {
    
    
    return x + y +z
}
export const a = 314

另一种写法:

function fn(x,y) {
    
    
    return x + y
}

function fn1(x,y,z) {
    
    
    return x + y +z
}
const a = 314

export {
    
     fn, fn1, a}

导入

按需

main.js

// 按需导入
import {
    
    a,b} from './module-b'
console.log(a,b)

你要提前知道在导出时,它们是不是叫a,b。

整体导入

// 按需导入(全部成员导入)
import * as all from './module-b'
console.log(all) // 收集了所有成员的变量(对象)

默认导出和按需导出共存

module-c.js

// 混合导入
//  同时有默认导出和 按需导出

const fn = function(x,y) {
    
    
    return x + y
}

const a = 100
const fn1 = function(x,y,z) {
    
    
    return x + y+z
}

// 按需导出
export {
    
    
    a, fn1
}

// 默认导出
export default {
    
    
    fn
}

main.js

// 有混合导入
import obj, {
    
    fn1} from './es6module/c.js'

// 有混合导入, 也可以拆开写
// import obj from './es6module/c.js'
// import {fn1} from './es6module/c.js'
console.log(obj,fn1)

导入导出重命名

一个名字在a.js中导出也是这个名字;

在main.js中也有一个名字与之重复了,此时就要改名字,有两个地方可改名字:

  • 在a.js中导出时改下名字
  • 在main.js中导入时改下名字
export {
    
    xxx as 新名字}

# 导入重命名
import {
    
    xxx as 新名字}
// tool.js
const fn = function(x,y) {
    
    
    return x + y
}
// 导出重命名
export {
    
    fn as f2}

// index.js
import {
    
    f2 as f1} from "./tool.js"

代码工作原理

在这里插入图片描述

引入bootstrap,改造app.vue

导入bootstrap到项目中

直接在public/index.html中引入bootstrap的样式即可

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- 引入第三样式 -->
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>
<body>
  <div id="app"></div>
</body>
</html>

改写src/app.vue

从示例代码中把基本的html结构复制过来。

container,col-md-2,col-md-10 都是在boostarp.css中要用到的class。

<template>
  <div class="container">
    <nav class="navbar navbar-inverse">
      <a href="#" class="navbar-brand">VUE-CLI案例</a>
    </nav>
    <div class="col-md-2">
      <div class="row">
        <div class="list-group">
            <a href="#" class="list-group-item active">英雄列表</a>
            <a href="#" class="list-group-item">装备列表</a>
            <a href="#" class="list-group-item">技能列表</a>
        </div>
      </div>
    </div>
    <div class="col-md-10">
      <div>
        <a href="#" class="btn btn-primary">添加英雄</a>
        <hr />
        <table class="table table-hover">
          <thead>
            <tr>
              <th>ID</th>
              <th>英雄名称</th>
              <th>英雄性别</th>
              <th>创建时间</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>101</td>
              <td>亚索</td>
              <td></td>
              <td>2019-02-10 10:50:12</td>
              <td>
                <button class="btn btn-success">编辑</button>
                &nbsp;
                <button class="btn btn-danger">删除</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>

<script>

export default {
     
     
  data () {
     
     
    return {
     
     
      
    }
  }
}
</script>

实现顶部通栏组件

分析布局:

在这里插入图片描述

创建组件

src/components/header.vue按boostrap样式要求,完成dom

<template>
  <nav class="navbar navbar-inverse">
    <a href="#" class="navbar-brand">VUE-LOADER + WEBPACK案例</a>
  </nav>
</template>

对于.vue而来,如果不写script项,则相当于一个默认导出。

在app.vue中引入组件

步骤:

  1. 导入
  2. 注册组件
  3. 在模板中使用
<template>
  <div id="app" class="container">
    <!-- 头部组件 -->
    <my-header></my-header>
    
    <div class="col-md-2">
      侧边栏 col-md-2 bootstrap中的栅格系统
    </div>
    <div class="col-md-10">
      路由容器
    </div>

  </div>
</template>

<script>
// 1.导入
import MyHeader from './components/header.vue'

// 默认导出
export default {
     
     
	// 2. 注册
  components: {
     
     
    MyHeader: MyHeader 
  }
}
</script>

实现左侧侧边栏组件

创建组件

src/components/sideBar.vue按boostrap样式要求,完成dom

<template>
  <div class="row">
    <div class="list-group">
        <a href="#" class="list-group-item active">英雄列表</a>
        <a href="#" class="list-group-item">装备列表</a>
        <a href="#" class="list-group-item">技能列表</a>
    </div>
  </div>
</template>

在app.vue中引入组件

步骤:

  1. 导入
  2. 注册组件
  3. 在模板中使用
<template>
  <div class="container">
    <!-- 头部组件 -->
    <my-header></my-header>
    <div class="col-md-2">
      <!-- 侧边栏组件 -->
      <my-side-bar></my-side-bar>
    </div>
    <div class="col-md-10">
      <div>
        <a href="#" class="btn btn-primary">添加英雄</a>
        <hr />
        <table class="table table-hover">
          <thead>
            <tr>
              <th>ID</th>
              <th>英雄名称</th>
              <th>英雄性别</th>
              <th>创建时间</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>101</td>
              <td>亚索</td>
              <td></td>
              <td>2019-02-10 10:50:12</td>
              <td>
                <button class="btn btn-success">编辑</button>
                &nbsp;
                <button class="btn btn-danger">删除</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>
<script>
// 1. 导入组件
import MyHeader from './components/header.vue'
import MySideBar from './components/sideBar.vue'
// 2. 注册组件
// 3. 使用组件
export default {
     
     
  // 2. 注册组件
  components: {
     
     
    MyHeader,
    MySideBar
  }
}
</script>

实现路由

目标

  • 在支持左侧边栏中的链接显示不同的内容
  • 在点击添加,编辑时显示不同的内容

思路

引入vue-router

配置路由规则

在根组件中添加router-view

给左侧添加声明式路由导航

基本步骤

  1. 安装vue-router

    vue-router是vue的插件,我们通过npm包的方式来加载(在.html中,是通过srcript 的src属性来加载的)

npm i vue-router
  1. 导入并注册插件

    main.js

import VueRouter from 'vue-router'

Vue.use(VueRouter) //注册插件(**重要**)
  1. 准备页面组件

src下创建pages文件夹,并在下面放置三个组件,分别表示路由切换时的三个页面:

src/pages/index.vue
src/pages/add.vue
src/pages/edit.vue

每个页面组件中,都只需要维持基本内容即可。

  1. 导入组件,初始化路由插件,路由规则配置

    main.js中

// 提前定义好三个组件,以备在路由中使用
import Index from './pages/index.vue'
import Add from './pages/add.vue'
import Edit from './pages/add.vue'
// 创建路由实例,定义路由规则
const router = new VueRouter( {
    
    
  routes: [
    {
    
     path: '/', component: Index },
    {
    
     path: '/add', component: Add }
  ]
})
  1. 把vueRouter挂载在vue实例上
new Vue({
    
    
  render: h => h(App),
  router: router
}).$mount('#app')
  1. 在根组件中,使用router-view设置路由出口

App.vue

<div class="col-md-10">
    <!-- 内容(路由进行切换) -->
    <router-view></router-view>
</div>

7.直接在地址栏中进行测试

http://127.0.0.1:10088/#/

http://127.0.0.1:10088/#/add

修改左侧导航组件

在components/MyAside.vue中,使用router-link替换之前的a。

<template>
  <div class="row">
    <div class="list-group">
      <router-link to="/" class="list-group-item">英雄列表</router-link>
      <router-link to="/assets" class="list-group-item">装备列表</router-link>
      <router-link to="/skills" class="list-group-item">技能列表</router-link>
      <!-- <a href="#" class="list-group-item active">英雄列表</a> -->
      <!-- <a href="#" class="list-group-item">装备列表</a>
      <a href="#" class="list-group-item">技能列表</a> -->
    </div>
  </div>
</template>

<style>
/*
router-link-exact-active: 在路由匹配成功时,会自动添加。
*/
a.list-group-item.router-link-exact-active {
     
     
  z-index: 2;
  color: #fff;
  background-color: #337ab7;
  border-color: #337ab7;
}
</style>

再次点击测试。

1.当前激活的路由导航链接会多出一个特殊的类名:router-link-exact-active 。根据它来设置高亮效果。

对应修改路由配置

// 入口文件

// import es6的模块化, 用来导入另一个模块
// 引入根组件
import app from './app.vue'
import Vue from 'vue'

import Index from './pages/index.vue'
import Add from './pages/add.vue'
import Assets from './pages/assets.vue'
import Skills from './pages/skills.vue'

// 1.导入包
import VueRouter from 'vue-router'
// 以vue插件的方式去引入vueRouter到vue中
// 2. 使用插件
Vue.use(VueRouter)

// 3.创建路由对象
const router = new VueRouter({
    
    
  routes: [
    // 重定向
    {
    
     path: '/', redirect: '/hero' },

    {
    
     path: '/hero', component: Index },
    {
    
     path: '/add', component: Add },

    // 装备管理
    {
    
     path: '/assets', component: Assets },
    // 技能管理
    {
    
     path: '/skills', component: Skills }
  ]
}) 

// 4. 在vue实例中使用

new Vue({
    
    
  router: router,
  // 这是Vue的配置项,名是render,值是一个函数。
  // 功能: 把app这个组件手挂载到vue实例 ------ vue实例中就只一个根组件
  render: function (h) {
    
     return h(app) }
}).$mount('#app')


// #app: 就是public/index.html中的div

工作原理小结

在这里插入图片描述

提取router/index.js

分析:

在main.js中涉及路由代码太多,有必须提练一下。

思路:

  • 把与路由相关的代码,提取出来,写在src/router/index.js中;

  • 在main.js中引入它

操作:

(1)新建 src/router/index.js,写入代码如下:

import Vue from 'vue'
// 引入路由
import VueRouter from 'vue-router'
// 注册插件
// Vue.use(插件) : 把这个插件的功能集成到vue
Vue.use(VueRouter)

import Index from '../pages/index.vue'
import Add from '../pages/add.vue'
import Assets from '../pages/assets.vue'
import Skills from '../pages/skills.vue'

const router = new VueRouter({
    
    
    routes: [
        {
    
     path: '/', component: Index },
        {
    
     path: '/add', component: Add },
        {
    
     path: '/assets', component: Assets },
        {
    
     path: '/skills', component: Skills }
    ]
})

export default router

注意一下:组件文件的路径关系。

(2) 在main.js中引入

// 入口文件
// import es6的模块化, 用来导入另一个模块
// 默认导入,引入根组件
import app from './app.vue'
// console.log(app)

// "vue"是我们之前npm i vue时下载的,它相当于是第三个模块,所以不需要加相对路径
// 其实,这里的vue来自:node_modules\vue\dist\vue.runtime.common.js
import Vue from 'vue'
import router from './router/index.js'
const vm = new Vue({
    
    
    // render:这是Vue的配置项(el, data,computed....),名是render,值是一个函数。
    // 功能: 把app这个组件手挂载到vue实例 ------ vue实例中就只一个根组件
    render: function(h) {
    
     return h(app) },
    router: router
})

vm.$mount('#app')

关系图

在这里插入图片描述

json-server模拟接口

json-server 是一个全局安装的工具,用来把.json快速生成RESTful风格的接口。

在前面的学习中,已经全局安装过。

建立目录mockdata,在下面放置一个db.json文件,内容如下:

|-src
|-src\mockdata\db.json

内容:

{
    
    
  "heroes":[
    {
    
     "id":10000, "heroName": "安妮", "gender": "女", "cTime": "2010-10-10 10:10:10" },
    {
    
     "id":10001, "heroName": "德玛西亚", "gender": "男", "cTime": "2014-10-10 10:10:10" },
    {
    
     "id":10002, "heroName": "刘三姐", "gender": "女", "cTime": "Fri Apr 17 2020 16:24:42 GMT+0800 (中国标准时间)" },
    {
    
     "id":10003, "heroName": "超人", "gender": "男", "cTime": "2020/10/10 10:10:10" }
  ]
}

在 db.json 的位置启动数据接口服务器:json-server db.json

在这里插入图片描述

验证一下:

在这里插入图片描述

在项目运行过程,不能关闭这个服务。

安装并使用axios

在项目中通过npm i axios安装axios,方便我们来获取json-server接口服务器中数据。

安装

之前是<script src='axios的地址'> 来使用axios的,现在要通过第三方包的方式来下载使用。

项目目录下运行如下命令:

npm i axios

结果:

+ [email protected]
added 4 packages from 7 contributors in 26.593s

使用

在src/pages/hero/index.vue中,测试axios的使用

通过click事件来测试使用。

<script>
// 引入axios去发ajax请求
import axios from 'axios'

axios({
    
    
  method: 'get',
  url: 'http://localhost:3000/heroes'
  // 参数....
})
</script>

在浏览器的network面板观察请求是否发出。

获取并显示数据

  • 补充一个方法loadHeros用来发ajax请求,获取数据,并保存到数据项中。

  • 在钩子函数 created中调用它

  • 显示数据

文件:src/pages/index.vue

<script>
// 使用axios
import  axios from 'axios'
export default {
    
    
  data () {
    
    
    return {
    
    
      heros: []
    }
  },
  created () {
    
    
    // 组件创建时,会自动执行一个函数
    this.loadData()
  },
  methods: {
    
    
    loadData () {
    
    
      axios({
    
    
        method: 'GET',
        url: 'http://localhost:3000/heroes'
      }).then(res => {
    
    
        console.log('后端求回来的数据是', res)
        // 把取回来的数据保存当前的数据项中
        this.heros = res.data
      })
    }
  }
}
</script>

根据数据进行渲染

<template>
    <div>
      <a href="#" class="btn btn-primary">添加英雄</a>
      <hr />
      <table class="table table-hover">
        <thead>
          <tr>
            <th>ID</th>
            <th>英雄名称</th>
            <th>英雄性别</th>
            <th>创建时间</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- 
            v-for 中 key 的作用:
            key的作用是为了区别于一条条记录(循环会产生多个)。 加了key的好处在于在vue的内部当数据更新时,会重新生成
            视图,而在这个过程,如果有key会加速这个生成的过程。

            key: 的值,可以字符串或者是整数。 序号,id,.....
           -->
          <tr v-for="(hero, idx) in heros" :key="hero.id">
            <td>{
   
   {idx}}</td>
            <td>{
   
   {hero.heroName}}</td>
            <td>{
   
   {hero.gender}}</td>
            <td>{
   
   {hero.cTime}}</td>
            <td>
              <button class="btn btn-success">编辑</button>
              &nbsp;
              <button class="btn btn-danger">删除</button>
            </td>
          </tr>

          <tr v-if="heros.length ===0">
            <td colspan="5">没有数据</td>
          </tr>
        </tbody>
      </table>
    </div>
</template>

注意:

  • key的使用

删除功能

在这里插入图片描述

实现大致步骤:

  1. 绑定点击事件
  2. 弹出确认框
  3. 点击确认,上传请求
  4. 删除成功,更新列表

src/page/index.vue

<td>
    <button class="btn btn-success">编辑</button>
    &nbsp;
    <button @click="hDelete(hero.id)" class="btn btn-danger" >删除</button>
</td>

补充一个做删除的方法:

hDelete (id) {
    
    
      const res = window.confirm('你确定要删除吗?')
      if(!res) {
    
    
        return 
      }
      // json-server时,它会自动会生成restful风格的接口
      // 它用来作删除的接口就是这样要求的
      axios({
    
    
        method: 'DELETE',
        url: 'http://localhost:3000/heroes/' + id
      }).then(res => {
    
    
        console.log(res)
        this.loadData()
      })
    },

添加功能

由于这个添加功能比较复杂,不方便与列表页做在一起,这里单独来设置一个页面组件来实现添加功能。

实现大致步骤:

  1. 将英雄列表组件,添加按钮改成router-link,跳转到 /add 路由地址。
  2. 定义路由规则,创建添加组件,指定这个添加组件。
  3. 完成添加组件中的基础布局(表单),双向绑定表单元素。
  4. 监听表单提交事件,调用添加接口,进行添加操作。
  5. 成功的时候:跳转到英雄列表组件。失败:错误提示。

准备页面组件和路由

页面

新增文件:src/pages/add.vue

<template>
  <div>
    <form>
        <legend>添加英雄</legend>
        <div class="form-group">
          <label>英雄名称</label>
          <input type="text"  class="form-control" />
        </div>
        <div class="form-group">
          <label>英雄性别</label>
          <div>
            <input type="radio" value="" ><input type="radio" value="" ></div>
        </div>
        <button class="btn btn-primary" >提交</button>
      </form>
  </div>
</template>

补充路由跳转链接

pages/index.vue

<router-link to="add" class="btn btn-primary">添加英雄</router-link>
或者:
<a href="#" class="btn btn-primary" @click.prevent="$router.push('/add')">添加英雄</a>

实现功能

  1. 添加v-model,给表单元素添加双向绑定 ,以获取用户输入的数据
  2. 添加button添加点击事件,提交ajax

heroAdd.vue

<template>
  <div>
    <form>
        <legend>添加英雄</legend>
        <div class="form-group">
          <label>英雄名称</label>
          <input type="text" class="form-control" v-model="hero.heroName" />
        </div>
        <div class="form-group">
          <label>英雄性别</label>
          <div>
            <input type="radio" v-model="hero.gender" value=""><input type="radio" v-model="hero.gender" value=""></div>
        </div>
        <button class="btn btn-primary" @click="hAdd">提交</button>
      </form>
  </div>
</template>

<script>
import axios from 'axios'
export default {
     
     
  data () {
     
     
    return {
     
     
      hero: {
     
     
        heroName: '无名',
        gender: '女'
      }
    }
  },
  methods: {
     
     
    hAdd () {
     
     
      // 收集表单的信息
      const {
     
      heroName,  gender} = this.hero
      // 检验有效性
      if (heroName === '') {
     
     
        alert('名字不能为空')
        return false
      }
      // ajax提交
      axios({
     
     
        method: 'POST',
        url: 'http://localhost:3000/heroes',
        data: {
     
     
          heroName,
          gender,
          cTime: Date.now() // 获取当前的时间戳
        }
      }).then(res => {
     
     
        // 跳回主页
        // console.log(res)
        this.$router.push('/')
      }).catch(err => {
     
     
        alert('添加失败')
        console.log(err)
      })
    }
  }
}
</script>

编辑功能

实现大致步骤:

  1. 在英雄列表,给编辑按钮绑定点击事件,跳转到编辑组件/hero/edit/:id
  2. 创建编辑组件,配置路由规则,使用的动态路由规则
  3. 完成编辑组件中的基础布局(表单),双向绑定表单元素。
  4. 在组件初始化的时候,获取当英雄数据,填充给表单。
  5. 监听表单的提交事件,进行编辑请求的发送。
  6. 成功:跳转英雄列表进行预览,失败:错误提示。

准备页面组件和路由

页面组件

新增:src/pages/edit.vue

<form>
    <legend>编辑英雄</legend>
    <div class="form-group">
      <label>英雄名称</label>
      <input type="text" class="form-control" />
    </div>
    <div class="form-group">
      <label>英雄性别</label>
      <div>
        <input type="radio" value=""/><input type="radio" value="" /></div>
    </div>
    <button class="btn btn-primary">提交</button>
  </form>

设置路由

router/index.js

+ import Edit from './pages/edit.vue'

const router = new VueRouter({
  routes: [
    // ...
+    { path: '/edit', component: Edit },

  ]
}) 

补充路由跳转

落地项目代码:

src/pages/index.vue

<td>
    <!-- 跳转到编辑页面,并传入当前要编辑的英雄的编号 
跳转页面带参数:  XXXXXX
-->
    <button @click="$router.push('/edit/'+id)" class="btn btn-success">编辑</button>

实现功能

思路:

1.发一次请求,根据id获取这个英雄的详情并显示出来

2.用户在此基础上进行修改,并提交数据。

显示初值

思路:

发一次请求,根据id获取这个英雄的详情并显示出来

要点:

  • 定义数据项,在表单元素进行双向绑定。
  • 定义loadDetail访求,根据id获取详情。
  • 在created中去调用loadHeroById
data () {
    
    
    return {
    
    
      hero: {
    
    
        gender: '',
        heroName: ''
      }
    }
  },
created () {
    
    
    this.loadDetail()
  },
  methods: {
    
    
    loadDetail() {
    
    
      axios({
    
    
        method: 'GET',
        url: 'http://localhost:3000/heroes/' + this.$route.query.id
      }).then(res => {
    
    
        console.log(res)
        // 保存到数据项中
        this.hero.gender = res.data.gender
        this.hero.heroName = res.data.heroName
      })
    }
 }

视图

<template>
  <form>
    <legend>编辑英雄</legend>
    <div class="form-group">
      <label>英雄名称</label>
      <input type="text" v-model="hero.heroName" class="form-control" />
    </div>
    <div class="form-group">
      <label>英雄性别</label>
      <div>
        <input type="radio" v-model="hero.gender" value="" /><input type="radio" v-model="hero.gender" value="" /></div>
    </div>
    <button class="btn btn-primary" @click.prevent="hModify">提交</button>
  </form>
</template>

实现保存

用户在页面上进行修改,点击保存,把改动通过axios调用接口,实现保存。

  • 给按钮添加click事件。
  • 在回调函数中:
    • 收集用户信息
    • 判空
    • 调用接口
<!-- 对于form中的button,它有提交表单的默认动作,这里要阻止 -->
<button @click.prevent="hModify" class="btn btn-primary">保存</button>
hModify () {
    
    
      // 收集表单的信息
      const {
    
     heroName,  gender} = this.hero
      // 检验有效性
      if (heroName === '') {
    
    
        alert('名字不能为空')
        return false
      }
      // ajax提交
      axios({
    
    
        method: 'PUT',
        url: 'http://localhost:3000/heroes/' + this.$route.query.id,
        data: {
    
    
          heroName,
          gender,
          cTime: Date.now() // 获取当前的时间戳
        }
      }).then(res => {
    
    
        // 跳回主页
        // console.log(res)
        this.$router.push('/')
      }).catch(err => {
    
    
        alert('添加失败')
        console.log(err)
      })
    }

如有不足,请多指教,
未完待续,持续更新!
大家一起进步!

猜你喜欢

转载自blog.csdn.net/qq_40440961/article/details/112994191