【转载】Vue 2.x 实战以后台管理系统开发(二)

2. 常见需求

01. 父子组件通讯

a. 父 -> 子(父组件传递数据给子组件)javascript

使用 props,具体查看文档 - 使用 Prop 传递数据(cn.vuejs.org/v2/guide/co…css

b. 父 -> 子(在父组件上调用子组件内的方法)html

使用 ref,具体查看文档 - 子组件索引(cn.vuejs.org/v2/guide/co…前端

<!--父组件 template--> <div id="parent"> <!--子组件--> <user-profile ref="profile"></user-profile> </div>
// 父组件 script this.$refs.profile.someMethod();

注意:若是在子组件上设置 ref 属性,则能够经过 this.$refs 获取到该子组件对象,若是在普通的 html 标签上设置 ref 属性,则获取到的是 Dom 节点。vue

c. 子 -> 父(在父组件上获取子组件内的数据)java

同上,也是利用 refwebpack

// 父组件 script let childData = this.$refs.profile.someData;

d. 子 -> 父(子组件内触发事件,父组件监听事件)web

父组件能够在使用子组件的地方直接用 v-on 来监听子组件触发的事件,具体查看文档 - 使用 v-on 绑定自定义事件(cn.vuejs.org/v2/guide/co…ajax

<!--父组件 template--> <div id="parent"> <!--子组件--> <user-profile @childTrigger="parentHandle"></user-profile> </div>
// 父组件 script methods: { parentHandle(params){ // 这个方法在子组件 emit childTrigger 事件后会执行 // params 为子组件里触发事件时传的参数 } }
// 子组件 user-profile script this.$emit('childTrigger', params);

e. 子 -> 父(子组件传值,父组件里使用,具体实现见 03vue-router

01总结:
应用场景示例:在父组件上打开侧边栏子组件,能够传 prop visible(true)来控制侧边栏打开;侧边栏内部有关闭按钮,就在点击关闭按钮后触发一个事件,父组件监听事件执行方法将 data visible 改成 false
PS:父组件传值到子组件,传的值是 Object 类型,子组件使用 v-model 能够修改该值(将某个表单元素的 v-model 设为该值),父组件可直接获取到改变后的值。

02. 全局函数

有时候会用到一些工具类函数,但愿能够全局调用,而不是局限于某个组件中。

Step 1:
项目根目录/static/js/ 目录下新建一个 util.js 文件,将经常使用的工具函数写在这里面。

Step 2:
index.html 里面引入 util.js,就能够在 .vue 文件里使用那些方法了,以下:

<body>
  <div id="app"></div> <!-- 引入经常使用 js 方法 --> <script type="text/javascript" src="/static/js/util.js"></script> <!-- built files will be auto injected --> </body>

02总结:
使用这个方法可使得一些使用频率高的函数能够在全部 .vue 文件中被调用,笨拙而又简单。

03. slot

之前看文档时一直不理解如何使用 slot,如今用多了 elementui 的组件以后,就渐渐发现了它的实用性。
简单来讲,使用 slot 可使咱们作到:在父组件里使用子组件时,在子组件里插入一些自定义的内容(html 代码啥的),具体查看文档:cn.vuejs.org/v2/guide/co…
更神奇的功能是 做用域插槽,可让咱们在父组件里使用子组件时,获取子组件传来的数据,具体查看文档:cn.vuejs.org/v2/guide/co…

简单应用示例

<!-- a-button 子组件 --> <button type="button" class="a-button" @click="$emit('btn-click')"><slot></slot></button> <!-- 这里监听了 button 的 click 事件,而后又触发了 btn-click 事件 -->
<!-- 父组件 --> <a-button @btn-click="handleClick">这里写的东西会覆盖子组件里的 slot 标签所在的位置</a-button> <!-- 这里监听了子元素触发的 btn-click 事件,而后执行 handleClick 函数 -->

渲染结果:

<button type="button" class="a-button">这里写的东西会覆盖子组件里的 slot 标签所在的位置</button>

能够应用简单的 slot 来达到为不一样的按钮填充文字的目的:

<a-button @click="handleClick">详情</a-button> <a-button @click="handleClick">搜索</a-button> <!-- 渲染结果 --> <button type="button" class="a-button">详情</button> <button type="button" class="a-button">搜索</button>

做用域插槽示例

<!-- 子组件 --> <div class="child"> <!-- slot 这个位置会在子组件被使用时被父组件传来的 html 代码覆盖 --> <!-- slot 上的 text/child 就至关于传给父组件的 props (假设 name 为子组件的 data,name: someChild) --> <slot text="hello from child" :child="name"></slot> </div>

在父级中,具备特殊属性 scope<template> 元素,表示它是做用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 prop 对象:

<!-- 父组件 --> <div class="parent"> <child> <template scope="props"> <span>hello from parent</span> <span>{{ props.text }}</span> <span>{{ props.child }}</span> </template> </child> </div>

渲染结果:

<div class="parent"> <div class="child"> <span>hello from parent</span> <span>hello from child</span> <span>someChild</span> </div> </div>

03总结:
应用场景示例:elementui 的 button 组件中有简单插槽的使用,table 组件则使用到了 做用域插槽

<!-- button 组件 -->
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="text">文字按钮</el-button>

<!-- table 组件 -->
<el-table
  :data="tableData">
  <el-table-column
    prop="zip"
    label="邮编"
    width="120">
  </el-table-column>
  <el-table-column
    fixed="right"
    label="操做"
    width="100">
    <template scope="scope">
      <el-button
        <!-- 能够经过 scope.$index 获取到当前行的索引 -->
        @click.native.prevent="deleteRow(scope.$index)">
        移除
      </el-button>
    </template>
  </el-table-column>
</el-table>

04. router 使用小记

vue-router 的使用,简单来讲就是经过配置,实如今不一样的 url 路径下,页面渲染不一样的组件。具体查看文档:vue-router 2

使用示例
一级路由:

<!-- App.vue --> <!-- 该组件为最高级父组件,使用 router-view 来根据路径肯定要显示的子组件 --> <template> <div id="app"> <!-- router-view 位置用来显示组件,如显示下面的 index.vue --> <router-view></router-view> </div> </template>

二级路由(路由可嵌套):

<!-- page/index.vue --> <!-- 该组件包含一个顶部栏和侧边菜单栏,内容区使用 router-view 来根据 url 路径显示子组件 --> <template> <div class="index"> <!-- 顶部导航条 --> <header class="main-header"> ... </header> <!-- /顶部导航条 --> <!-- 侧边导航栏 --> <aside class="main-sidebar sidebar-gradient"> ... </aside> <!-- /侧边导航栏 --> <!-- 根据页面一级菜单的点击而进行切换的内容区 --> <transition name="fade"> <!-- router-view 位置用来显示组件 --> <router-view></router-view> </transition> <!-- /内容区 --> </div> </template>

router 配置:

// router/index.js import Vue from 'vue'; import Router from 'vue-router'; // 引入组件 import index from 'page/index'; // 该组件包含一个顶部栏和侧边菜单栏,内容区使用 router-view 来根据 url 路径显示子组件 import notFoundComponent from 'page/404'; // 该组件为 404 页面,当你路由使用 history 模式时须要用到 import monitorIndex from 'page/monitor/index'; // 该组件为一个监控页面,用于显示在 page/index.vue 页面上的 router-view 处(即页面的内容区域) Vue.use(Router); // 定义 scrollBehavior 方法 const scrollBehavior = (to, from, savedPosition) => { if (savedPosition) { return savedPosition } else { return { x: 0, y: 0 } } } export default new Router({ mode: 'history', // mode 默认 hash 值,可是 hash (url中包含 # 符号)不太好看也不符合咱们通常的网址浏览习惯 // 当你使用 history 模式时,URL 就像正常的 URL,例如 http://yoursite.com/user/id,也好看! linkActiveClass: 'active', // 默认值: 'router-link-active',就是当前组件被激活,相应路由会自动添加类 'router-link-active',这里是为了全局设置激活类名,若是不设置,直接用默认的也是能够的 // 如:使用 router-link 组件来导航,经过传入 `to` 属性指定连接 // <router-link to="/foo">Go to Foo</router-link> // <router-link> 默认会被渲染成一个 `<a>` 标签,'/foo' 路由下的组件显示时,该 a 标签上会自动添加类 'active' scrollBehavior: scrollBehavior, // 经过这个属性(是个函数),可让应用像浏览器的原生表现那样,在按下 后退/前进 按钮时,简单地让页面滚动到顶部或原来的位置,若是不设置,则组件切换时滚动条位置不变 routes: [ { // 一级路由 path: '/', component: index, children: [ // 二级路由 // -----------默认首页------------- // 当 / 匹配成功,monitorIndex 会被渲染在 index 的 <router-view> 中 { path: '', component: monitorIndex, alias: 'index.html' }, // 这里的 alias 'index.html' 为当前页面的别名 // http://localhost:8080/index.html 等同于 http://localhost:8080/ // -----------监控中心------------- { // 当 /monitor 匹配成功, // monitorIndex 会被渲染在 index 的 <router-view> 中 path: 'monitor', name: '监控中心', component: monitorIndex } ] }, // 同一个路径能够匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高 // 所以下面的路由配置为备用,若是某个路径未被配置显示相应的组件,则显示 404 页面 { path: '*', component: notFoundComponent } ] });

引入 router 配置:

// main.js import Vue from 'vue'; // 引入 element ui 组件 import { Dropdown, DropdownMenu ...} from 'element-ui'; // 引入 App.vue import App from './App'; // 引入 router 配置 import router from './router'; // 默认会找到 router 文件夹下的 index.js 文件 // 引入项目图标的 sprite css,能够简单的经过这种方式引入 css 文件 import './assets/css/sprite.css' // 使用 element ui 组件 Vue.use(Dropdown) Vue.use(DropdownMenu) ... new Vue({ el: '#app', router, // 使用 router 配置 template: '<App/>', components: { App }, });

04总结:
关于 vue-router 的使用,看文档通常都能解决你的疑问,vue-router 2
其余参考文章:Vue.js系列之vue-router(中)(4)
PS:使用 history 模式的话,还须要 后台配置 支持。由于咱们的应用是个单页客户端应用,若是后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404(由于的确找不到该页面),这就很差看了。而且在后台配置后,还须要前端来提供 404 页面,我上面的示例代码中有提到,可供参考。

05. 测试接口

使用 Vue 开发单页应用时,先后端分离开发,进度不一。所以前端有时候就须要本身模拟接口的 json 文件,而后直接使用异步请求方法(如 ajax) 去获取数据,渲染页面,测试代码等。

Step 1:
项目根目录/static/api/ 目录下新建一个 test.json 文件,写入模拟的接口数据:

{
  "status": true,
  "data": {
    ...
  }
}

Step 2:
.vue 组件文件里任意须要请求数据的方法里(如 created 钩子,或者某个 methods 方法里)编写相关代码:

let vm = this; // ajax 请求数据 $.ajax({ type: 'GET', url: 'static/api/test.json', data: '', beforeSend: function() { // 显示 Loading vm.loading = true; }, complete: function() { // 隐藏 Loading vm.loading = false; }, success: function(data) { // 处理返回数据 ... }, error: function() { // 数据请求失败,给用户适当的反馈信息 ... }, dataType: 'json' });

05总结:
在后端还没有提供接口时,我都是用这个方法来测试前端获取数据和处理数据的代码是否正确。

3. 零碎问题

01. prop 传值小技巧

咱们能够为组件的 props 指定验证规格。若是传入的数据不符合规格,Vue 会发出警告。当组件给其余人使用时,这颇有用。

示例以下:

props: {
    // 基础类型检测 (`null` 意思是任何类型均可以) propA: Number, // 多种类型 propB: [String, Number], // 必传且是字符串 propC: { type: String, required: true }, // 数字,有默认值 propD: { type: Number, default: 100 }, // 数组/对象的默认值应当由一个工厂函数返回 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { return value > 10 } } }

愚蠢的我每次想要传 Number 或者 Boolean 类型的值到子组件时,都在父组件里定义好值,而后再绑定到子组件上:

// 这样会报错,由于 show type 为 Boolean,rows type 为 Number // 默认状况下直接传值,子组件接收到的都是 String 类型 // template <child show="true" rows="6"></child>
// 因而我这样作: // template <child :show="show" :rows="rows"></child> // script show: true, rows: 6
// 实际上能够直接这样作: // template <child :show="true" :rows="6"></child> // 官网如是说:若是想传递一个实际的 number,须要使用 v-bind ,从而让它的值被看成 JavaScript 表达式计算。

小技巧:当某个 prop 类型为 Boolean 时,能够直接把该 prop 的名称写在组件上,默认会传 true,不写的话默认为 false。好比 <child show :rows="6"></child> 这么写,子组件内部就能收到 show 为 true。

02. autoprefixer

有些人会问如何在项目里使用 autoprefixer 插件,事实上使用 vue-cliwebpack 模板生成的项目里已经带有 autoprefixer 的使用了,以下图:

 

autoprefixer

 

03. build 时不生成 .map 文件

对项目进行 npm run build 操做后,发现生成的文件超大(比想象中的大),尤为是那些 .map 文件,不过,咱们能够经过配置选择不生成该类文件。

// 项目根目录/config/index.js var path = require('path') module.exports = { build: { ... productionSourceMap: false, // 将该值设为 false,就不会生成 .map 文件了 // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], ... }, dev: { ... } }

原文连接:https://juejin.im/post/58f37bfe5c497d006c90ca28

相关文章
相关标签/搜索