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>
<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中引入组件
步骤:
- 导入
- 注册组件
- 在模板中使用
<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中引入组件
步骤:
- 导入
- 注册组件
- 在模板中使用
<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>
<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
给左侧添加声明式路由导航
基本步骤
-
安装vue-router
vue-router是vue的插件,我们通过npm包的方式来加载(在.html中,是通过srcript 的src属性来加载的)
npm i vue-router
-
导入并注册插件
main.js
import VueRouter from 'vue-router'
Vue.use(VueRouter) //注册插件(**重要**)
- 准备页面组件
在src下创建pages文件夹,并在下面放置三个组件,分别表示路由切换时的三个页面:
src/pages/index.vue
src/pages/add.vue
src/pages/edit.vue
每个页面组件中,都只需要维持基本内容即可。
-
导入组件,初始化路由插件,路由规则配置
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 }
]
})
- 把vueRouter挂载在vue实例上
new Vue({
render: h => h(App),
router: router
}).$mount('#app')
- 在根组件中,使用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>
<button class="btn btn-danger">删除</button>
</td>
</tr>
<tr v-if="heros.length ===0">
<td colspan="5">没有数据</td>
</tr>
</tbody>
</table>
</div>
</template>
注意:
- key的使用
删除功能
实现大致步骤:
- 绑定点击事件
- 弹出确认框
- 点击确认,上传请求
- 删除成功,更新列表
src/page/index.vue
<td>
<button class="btn btn-success">编辑</button>
<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()
})
},
添加功能
由于这个添加功能比较复杂,不方便与列表页做在一起,这里单独来设置一个页面组件来实现添加功能。
实现大致步骤:
- 将英雄列表组件,添加按钮改成router-link,跳转到
/add
路由地址。 - 定义路由规则,创建添加组件,指定这个添加组件。
- 完成添加组件中的基础布局(表单),双向绑定表单元素。
- 监听表单提交事件,调用添加接口,进行添加操作。
- 成功的时候:跳转到英雄列表组件。失败:错误提示。
准备页面组件和路由
页面
新增文件: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>
实现功能
- 添加v-model,给表单元素添加双向绑定 ,以获取用户输入的数据
- 添加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>
编辑功能
实现大致步骤:
- 在英雄列表,给编辑按钮绑定点击事件,跳转到编辑组件
/hero/edit/:id
。 - 创建编辑组件,配置路由规则,使用的动态路由规则
- 完成编辑组件中的基础布局(表单),双向绑定表单元素。
- 在组件初始化的时候,获取当英雄数据,填充给表单。
- 监听表单的提交事件,进行编辑请求的发送。
- 成功:跳转英雄列表进行预览,失败:错误提示。
准备页面组件和路由
页面组件
新增: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)
})
}
如有不足,请多指教,
未完待续,持续更新!
大家一起进步!