Сегодняшние разработки, такие как платформа управления для внутреннего использования, в основном идут в спешке. На самом деле использование webpack + vue для разработки значительно повысило эффективность. Но для нашего уровня развития. Есть еще много мест, где мы можем снова повысить эффективность разработки наших проектов и позволить нам больше сосредоточиться на бизнесе, в конце концов, время — это жизнь. Давайте обсудим их один за другим.
Разумное использование Webpack
Webpack
Это основа для реализации нашей предварительной разработки проекта, но на самом деле его полезность гораздо больше.Webpack
Приходите и помогите нам сделать что-то автоматизированное. Сначала нам нужно понятьrequire.context()
этот API
require.context()
ты можешь использовать этотребуют.контекст()Функции создают свои собственные контексты. Он позволяет вам передать каталог для поиска, флаг, указывающий, следует ли искать в подкаталогах, и регулярное выражение для сопоставления файлов.
ФактическиWebpack
путем разбораrequire()
вызов для извлечения следующей информации:
Directory: ./template
Regular expression: /^.*\.ejs$/
Затем, чтобы создать свой собственный контекст, что вы имеете в виду, то есть我们可以通过这个方法筛选出来我们需要的文件并且读取
Давайте кратко рассмотрим, как его использовать:
/**
* @param directory 要搜索的文件夹目录不能是变量,否则在编译阶段无法定位目录
* @param useSubdirectories 是否搜索子目录
* @param regExp 匹配文件的正则表达式
* @return function 返回一个具有 resolve, keys, id 三个属性的方法
resolve() 它返回请求被解析后得到的模块 id
keys() 它返回一个数组,由所有符合上下文模块处理的请求组成。
id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到
*/
require.context('demo', useSubdirectories = false, regExp = /\.js$/)
// (创建了)一个包含了 demo 文件夹(不包含子目录)下面的、所有文件名以 `js` 结尾的、能被 require 请求到的文件的上下文。
Не запутайтесь, давайте обсудим, как использовать это в проекте.
Маршрутизация организации
дляVue
Маршрутизация в , с которой все знакомы, похожа на декларативный файл конфигурации, который на самом деле очень лаконичен. Теперь давайте сделаем это более кратким
- раздельная маршрутизация
Прежде всего, чтобы облегчить наше управление, мы поставилиrouter
Файлы в каталоге разделены на следующую структуру
router // 路由文件夹
|__index.js // 路由组织器:用来初始化路由等等
|__common.js // 通用路由:声明通用路由
|__modules // 业务逻辑模块:所以的业务逻辑模块
|__index.js // 自动化处理文件:自动引入路由的核心文件
|__home.js // 业务模块home:业务模块
|__a.js // 业务模块a
-
modules
Обработка бизнес-модулей в папках
modules
В папке хранятся все наши модули бизнес-логики.Что касается того, как разделить модули бизнес-логики, я считаю, что у каждого, естественно, есть свой набор стандартов. Проходим вышеупомянутыйrequire.context()
Далее напишите основную часть автоматизацииindex.js
.
const files = require.context('.', true, /\.js$/)
console.log(files.keys()) // ["./home.js"] 返回一个数组
let configRouters = []
/**
* inject routers
*/
files.keys().forEach(key => {
if (key === './index.js') return
configRouters = configRouters.concat(files(key).default) // 读取出文件中的default模块
})
export default configRouters // 抛出一个Vue-router期待的结构的数组
После того, как часть автоматизации написана, как написать часть бизнес-компонента? это проще
import Frame from '@/views/frame/Frame'
import Home from '@/views/index/index'
export default [
// 首页
{
path: '/index',
name: '首页',
redirect: '/index',
component: Frame,
children: [ // 嵌套路由
{
path: '',
component: Home
}
]
}
]
-
common
обработка маршрута В нашем проекте есть много общедоступных маршрутов, которые необходимо обработать, например404
Ах,503
Ах, подождите, мы все в путиcommon.js
обработано в.
export default [
// 默认页面
{
path: '/',
redirect: '/index',
hidden:true
},
// 无权限页面
{
path: '/nopermission',
name: 'nopermission',
component: () => import('@/views/NoPermission')
},
// 404
{
path: '*',
name: 'lost',
component: () => import('@/views/404')
}
]
- инициализация маршрута Это наш последний шаг для инициализации маршрутизации нашего проекта.
import Vue from 'vue'
import VueRouter from 'vue-router'
import RouterConfig from './modules' // 引入业务逻辑模块
import CommonRouters from './common' // 引入通用模块
Vue.use(VueRouter)
export default new VueRouter({
mode: 'history',// 需要服务端支持
scrollBehavior: () => ({ y: 0 }),
routes: RouterConfig.concat(CommonRouters)
})
Предполагается, что некоторые друзья написали этот код и не знают, в чем его преимущества. Опишем такой сценарий, как разделение модулей по этой структуре. Нормальная ситуация в том, что мы создалиhome.js
Чтобы вручную поставить этот модульimport
Перейдите в место, указанное в файле маршрутизации, чтобы использовать его. Но с вышеперечисленнымindex.js
, когда вы используете его, вам просто нужно создатьhome.js
и бросаетVueRouter
Канонический массив, а остальное игнорируется.import RouterConfig from './modules' // 引入业务逻辑模块
Это было сделано для вас. Кроме того, вы можете продлитьhooks
Вынесите его в отдельный файл.
Единая декларация глобального компонента
Таким же образом, с приведенным выше опытом, давайте разберемся с нашими глобальными компонентами в соответствии с картиной тыквы. Это не о чем говорить, сразу переходим к основному коду
- организационная структура
components // 组件文件夹
|__xxx.vue // 其他组件
|__global // 全局组件文件夹
|__index.js // 自动化处理文件
|__demo.vue // 全局demo组件
-
global
иметь дело с
import Vue from 'vue'
let contexts = require.context('.', false, /\.vue$/)
contexts.keys().forEach(component => {
let componentEntity = contexts(component).default
// 使用内置的组件名称 进行全局组件注册
Vue.component(componentEntity.name, componentEntity)
})
- использование и инструкции
Это проще в использовании, непосредственно вapp.js
Просто процитируйте этот файл.
Уведомление: я видел, как некоторые люди используют имена компонентов, чтобы отличить глобальные компоненты от обычных компонентов, а затем используют регулярные выражения, чтобы определить, требуется ли глобальная регистрация. Я помещаю глобальные компоненты непосредственно вglobal
папка, то напрямую используется зарегистрированное имя компонентаcomponent.name
. Какой метод использовать, зависит от человека.
Получите максимум от NodeJS
Оставь этоnode
Не использовать такую хорошую вещь немного напрасно, так что давайте посмотримnode
Что мы можем сделать, чтобы повысить нашу эффективность.
Есть такой сценарий, каждый раз при создании модуля приходится создавать новыйvue
файл и соответствующийrouter
Конфигурация и большая часть новой страницы аналогичны, и вам придется копировать и вставлять другие страницы. Подумайте об этом немногоlow
. Так как естьnode
Можем ли мы пройтиnode
Чтобы сделать этот беспорядок письма? Давайте воплотим наши идеи в жизнь.
Мы реализуем эту функцию главным образом посредствомNode
изfsа такжеprocess, если вам интересно, вы можете изучить его подробно.
Сначала мы должны написать нашnode
Скрипт, вот более простая версия. Нечего проверять папки или файлы, просто реализовать нашу идею:
/*
* fast add new module script
*/
const path = require('path')
const fs = require('fs')
const chalk = require('chalk')
const reslove = file => path.resolve(__dirname, '../src', file)
// symbol const
const RouterSymbol = Symbol('router'),
ViewsSymbol = Symbol('views')
// root path
const rootPath = {
[RouterSymbol]: reslove('router/modules'),
[ViewsSymbol]: reslove('views')
}
//loggs
const errorLog = error => console.log(chalk.red(`${error}`))
const defaultLog = log => console.log(chalk.green(`${log}`))
// module name
let moduleName = new String()
let fileType = new String()
//const string
const vueFile = module => (`<template>
</template>
<script>
export default {
name: '${module}',
data () {
return {
}
},
methods: {
},
created() {
}
}
</script>
<style lang="less">
</style>
`)
// route file
const routerFile = module => (`// write your comment here...
export default [
{
path: '/${module}',
name: '',
redirect: '/${module}',
component: () => import('@/views/frame/Frame'),
children: [
{
path: '',
fullPath: '',
name: '',
component: () => import('@/views/${module}/index')
}
]
}
]
`)
/**
* generate file
* @param {*} filePath
* @param {*} content
* @param {*} dirPath
*/
const generateFile = async (filePath, content, dirPath = '') =>{
try {
// create file if file not exit
if (dirPath !== '' && ! await fs.existsSync(dirPath)) {
await fs.mkdirSync(dirPath)
defaultLog(`created ${dirPath}`)
}
if (! await fs.existsSync(filePath)) {
// create file
await fs.openSync(filePath, 'w')
defaultLog(`created ${filePath}`)
}
await fs.writeFileSync(filePath, content, 'utf8')
} catch (error) {
errorLog(error)
}
}
// module-method map
const generates = new Map([
['view', async (module) => {
// module file
const filePath = path.join(rootPath[ViewsSymbol], module)
const vuePath = path.join(filePath, '/index.vue')
await generateFile(vuePath, vueFile(module), filePath)
}],
// router is not need new folder
['router',async (module) => {
const routerPath = path.join(rootPath[RouterSymbol], `/${module}.js`)
await generateFile(routerPath, routerFile(module))
}]
])
defaultLog(`请输入模块名称(英文):`)
// files
const files = ['view', 'router']
// 和命令行进行交互 获取的创建的模块名称
process.stdin.on('data', (chunk) => {
try {
if (!moduleName) {
moduleName = chunk
} else {
chunk = chunk.slice(0,-2) // delete /n
defaultLog(`new module name is ${chunk}`)
files.forEach(async (el, index) => {
// 执行创建语句
await generates.get(`${el}`).call(null, chunk.toString())
if (index === files.length-1) {
process.stdin.emit('end')
}
})
}
} catch (error) {
errorLog(error)
}
})
process.stdin.on('end', () => {
defaultLog('create module success')
})
Ниже мы рассмотрим используемый процесс
Итак, мы создаемvue
а такжеrouter
файл, и содержимое было введено. Согласно нашим предварительно заявленным компонентам
Примечание. Это всего лишь простая идея, с мощными возможностями обработки файлов Node мы можем сделать гораздо больше.
Раскройте всю мощь миксинов
Vue
середина混入
mixinsявляется дистрибутивомVue
Многоразовые компоненты функционируют очень гибкий способ. Я слышал, что это может быть реализовано в виде крючков версии 3.0, но это не мешает ему сильным. Вы можете увидеть часть Фондаздесь. Здесь мы в основном обсуждаемmixins
Вы можете помочь нам в любой ситуации.
Общие миксины
Если у нас есть большое количество страниц таблицы, вы обнаружите, что многие вещи можно использовать повторно, если внимательно посмотреть, например分页
,表格高度
,加载方法
,laoding声明
Жду много дел. Разберем простой общий миксинlist.js
const list = {
data () {
return {
// 这些东西我们在list中处理,就不需要在每个页面再去手动的做这个了。
loading: false, // 伴随loading状态
pageNo: 1, // 页码
pageSize: 15, // 页长
totalCount: 0, // 总个数
pageSizes: [15, 20, 25, 30], //页长数
pageLayout: 'total, sizes, prev, pager, next, jumper', // 分页布局
list: []
}
},
methods: {
// 分页回掉事件
handleSizeChange(val) {
this.pageSize = val
// todo
},
handleCurrentChange (val) {
this.pageNo = val
// todo
},
/**
* 表格数据请求成功的回调 处理完公共的部分(分页,loading取消)之后把控制权交给页面
* @param {*} apiResult
* @returns {*} promise
*/
listSuccessCb (apiResult = {}) {
return new Promise((reslove, reject) => {
let tempList = [] // 临时list
try {
this.loading = false
// todo
// 直接抛出
reslove(tempList)
} catch (error) {
reject(error)
}
})
},
/**
* 处理异常情况
* ==> 简单处理 仅仅是对表格处理为空以及取消loading
*/
listExceptionCb (error) {
this.loading = false
console.error(error)
}
},
created() {
// 这个生命周期是在使用组件的生命周期之前
this.$nextTick().then(() => {
// todo
})
}
}
export default list
Ниже мы используем это непосредственно в компонентеmixins
import mixin from '@/mixins/list' // 引入
import {getList} from '@/api/demo'
export default {
name: 'mixins-demo',
mixins: [mixin], // 使用mixins
data () {
return {
}
},
methods: {
// 加载列表
load () {
const para = {
}
this.loading = true
getList(para).then((result) => {
this.listSuccessCb(result).then((list) => {
this.list = list
}).catch((err) => {
console.log(err)
})
}).catch((err) => {
this.listExceptionCb(err)
})
}
},
created() {
this.load()
}
}
использовалmixins
После этого простойloadoing
, 分页
,数据
Таблица, вероятно, нуждается только в приведенном выше коде.
миксины для управления публичными данными
Иногда у нас есть какие-то общие данные, они могут использоваться 3 или 4 модулями, но не достигают глобального масштаба. В это время мы можем использоватьmixins
Например, для управления ими у нас есть несколько модулей для использования этого списка типов пользователей, давайте посмотрим, как использоватьmixins
для достижения обмена.
// types.js
import {getTypes} from '@/api/demo' // ajax
export default {
data () {
return {
types: [] // ==> {name: '', value: ''}
}
},
methods: {
// 获取列表
getAllTypesList () {
getApiList().then((result) => {
// todo
this.types = result // 假设result就是我们需要使用的数据
}).catch((err) => {
console.error(err)
})
}
},
created() {
// 在需要使用这个mixins的时候取自动请求数据 这个可要可不要 你想在父组件中执行也是ok的
this.getAllTypesList()
}
}
ссылка в компоненте
import typeMixin from '@/mixins/types'
export default {
name: 'template',
mixins: [typeMixin],
data () {
return {
// types这个数组在使用组件中不用多余的定义,直接拿来用就行
type: ''
}
},
methods: {
}
}
Что касаетсяmixins
Полученные данные можно использовать непосредственно в компоненте
<!-- -->
<el-select v-model="type" clearable placeholder="请选择类型">
<el-option v-for="item in types" :key="item.id" :label="item.templateName" :value="item.id"></el-option>
</el-select>
Мы можем сделать это безvuex
для управления данными, которые повторно используются только несколько раз между модулями,И очень удобно получать нужные нам данные, даже определение опущено. Но у этого есть обратная сторона. То есть данные будут запрашиваться каждый раз заново. Если вас не волнует этот маленький изъян, я думаю, что его вполне можно использовать.
Уведомление: mixins
Это конечно просто, но комментарии и цитаты нужно делать хорошо, иначе новые участники, входящие в команду, могут запутаться, а это не способствует последующему сопровождению. Тоже палка о двух концах. Также: глобальныйmixins
Его нужно использовать с осторожностью, если в этом нет необходимости, я все же не рекомендую.
Дальнейшая инкапсуляция компонентов
Всем известно, что самым большим преимуществом компонентизации является высокая степень повторного использования и гибкость. Но как инкапсулировать компоненты и насколько это сделать удобнее для нас. На это нет стандартного ответа. у нас есть только高内聚,低耦合
Эта руководящая идеология используется для инкапсуляции общих компонентов нашего бизнеса, делая структуру нашей бизнес-страницы более лаконичной и ускоряя эффективность нашей разработки. Если инкапсулировать немного больше, страница может стать такой:
<template>
<box-content>
<!-- 头部标题部分 -->
<page-title>
<bread slot="title" :crumbs="[{name: 'xx管理', path: '', active: true, icon: ''}, {name: 'xxxx', path: '', active: true, icon: ''}]"></bread>
</page-title>
<!-- 表格部分 -->
<div>
<base-table v-loading="loading" :columns="headers" :list="list" :page-no ="pageNo" :page-size="pageSize" :total-count="totalCount" @delete="deleteItm" @change-size="handleSizeChange" @change-page="handleCurrentChange">
</base-table>
</div>
</box-content>
</template>
Кое-что ясно с первого взгляда.
компоненты без сохранения состояния
Легче всего вызвать у нас желание инкапсулировать безгражданство.HTML
компоненты, такие как мы удаляемheader
, menu
Послеcontent
часть. Ничто не требует сложного взаимодействия, но мы должны написать каждую страницу. Кто вы говорите, если вы не используете его, чтобы открыть нож🔪
<template>
<div class="container-fluid" :class="[contentClass]">
<el-row>
<el-col :span="24">
<!-- box with #fff bg -->
<div class="box">
<div class="box-body">
<slot></slot>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
Вышеупомянутая обработка очень проста, но вы будете использовать ее в проекте очень часто, поэтому этот пакет необходим.
Пакет компонентов таблицы ElementUI
ElementUI
На самом деле компонент Zhongde очень хорошо инкапсулирован, но при использовании таблицы остается много кода, который не нужно повторно писать в бизнесе. Я думаю, что это почти то же самое, что инкапсулировать форму, полагаться на конфигурацию для написания формы.demo
<template>
<el-row>
<el-col :span="24">
<el-table :data="list" border size="mini" @selection-change="handleSelectionChange" :max-height="tableHeight" v-bind="$attrs"> <!-- -->
<template v-for="(column, index) in columns">
<slot name="front-slot"> </slot>
<!-- 序号 -->
<el-table-column :key="index" v-if="column.type === 'selection'" type="selection" width="55"> </el-table-column>
<!-- 复选框 -->
<el-table-column :key="index" v-else-if="column.type === 'index'" type="index" width="50" label="序号"> </el-table-column>
<!-- 具体内容 -->
<el-table-column :key="index" v-else align="left" :label="column.title" :width="column.width">
<template slot-scope="scope">
<!-- 仅仅显示文字 -->
<label v-if="!column.hidden"> <!-- 如果hidden为true的时候 那么当前格可以不显示,可以选择显示自定义的slot-->
<!-- 操作按钮 -->
<label v-if="column.type === 'operate'">
<a href="javascript:void(0)" class="operate-button" v-for="(operate, index) in column.operates" :key="index" @click="handleClick(operate, scope.row)">
{{operate.name}}
</a>
</label>
<span v-else>
{{scope.row[column.key]}}
</span>
</label>
<!-- 使用slot的情况下 -->
<label v-if="column.slot">
<!-- 具名slot -->
<slot v-if="column.slot" :name="column.slot" :scope="scope"></slot>
</label>
</template>
</el-table-column>
</template>
<!--默认的slot -->
<slot/>
</el-table>
</el-col>
</el-row>
</template>
export default {
name: 'base-table',
props: {
// 核心数据
list: {
type: Array,
default: () => []
},
// columns
columns: {
type: Array,
required: true,
default: () => []
}
},
data () {
return {
tableHeight: xxx
}
},
methods: {
// 处理点击事件
handleClick(action, data) {
// emit事件
this.$emit(`${action.emitKey}`, data)
}
}
}
использовать:
<base-table v-loading="loading" :columns="headers" :list="list" @view="viewCb">
<!-- 自定义的slot -->
<template slot="demoslot" slot-scope="{scope}">
<span>
{{scope.row}}
</span>
</template>
<!-- 默认的slot 如果交互很复杂 我们还可以直接使用表格内部的组件 -->
<el-table-column
label="操作"
width="200"
>
<template slot-scope="scope">
<a href="javascript:void(0)" @click="defaultSlot(scope.row)">xxx</a>
</template>
</el-table-column>
</base-table>
export default {
name: 'table-demo',
data () {
return {
// 表格头部配置
headers: [
{ key: 'xxx', title: '测试' },
{ title: 'xxx', hidden: true, slot: 'demoslot'},
{
title: '操作', type: 'operate',
operates: [
{name: '详情',emitKey: 'view'}
]
}
]
}
},
methods: {
viewCb(){
// todo
},
defaultSlot(){
// todo
}
}
}
Форма, упакованная таким образом, не должна быть проблемой для удовлетворения некоторых основных потребностей. Что касается особых требований, то их можно совершенствовать шаг за шагом.
оElement-UI
По инкапсуляции табличных компонентов я планирую написать еще одну статью, чтобы подробно интерпретировать мои идеи и более сложные реализации.
Суммировать
Эти вещи не являются синтаксическим сахаром, но могут реально ускорить нашу эффективность в проекте. Избавьте себя и всю нашу команду от повторяющихся копипастов. Что касается скорости и качества. Я думаю, качество контроля использования общедоступных компонентов будет выше. Я предлагаю, чтобы общественные комментарии были написаны всесторонне и подробно, что может значительно сократить наши расходы на связь. Что касается упаковки компонентов, то это зависит от вашего бизнеса.
Вышеизложенные мнения являются сугубо личными мнениями, если есть какие-то ошибки, спасибо за их исправление.
Я не ожидал, что это увидит так много людей, я работал сверхурочно, чтобы разобратьсяобразец кода, Образец кода также является профессиональной версией, разработанной в бэкэнде Vue.Если бизнес-требования верны, вы можете использовать его напрямую 😂😂
исходный адресСтавьте ⭐, если считаете это полезным