-
Notifications
You must be signed in to change notification settings - Fork 3
Description
PPT - 前端项目开发工程化探索.pdf
PPT由 Markdown Preview Enhanced 生成
presentation:
width: 1920
height: 1080
theme: league.css
minScale: 0.2
maxScale: 2
slideNumber: true
mouseWheel: true
showNotes: true
enableSpeakerNotes: true
center: true
前端项目开发工程化探索
2020.08.07
内容
- 现阶段遇到的问题 ?
- 工程化探索
- 搭建“管理系统业务”项目技巧
- 目录结构约定
- Vue CLI4 生成项目工程
- 工程配置
- 编辑器
- 编码规范
- 常规编码规范与技巧
- Vue 编码常识性规范
- Vue 开发小技巧
- Vue 项目性能调优
- 常见业务设计与注意问题
现阶段遇到的问题?
- 不只是一个项目的问题
- 业务复杂:多功能、多页面、多状态
- 多人协作、团队协作
- 开发效率
- 开发质量
- 代码可维护性
什么是前端工程化 ?
工程化探索需要关注的东西
- 生成标准项目目录
- 整合工程化优秀方案
- 代码压缩、资源打包、代码检查、本地调试、开发工具,编辑器插件、代码提交与格式化、代码推送、···
- 易于扩展、协同开发
- 模块化、组件化
- 规范化、自动化
建立规范
-
文件目录结构
-
代码规范
-
组件管理
-
模块管理
-
接口规范
-
commit规范
-
code review
-
......
管理系统业务项目搭建技巧
一切实际从业务出发
思考业务常见的业务场景 🤔
-
参考模块 - vue-iview-admin-template
-
开发者建议 - 文档
目录结构约定
参考结构:
目录约定
文件目录结构化、项目业务模块化、文件结构遵循就近原则
1. JS
文件
所有的 .js
文件都遵循横线连接 kebab-case
。
-
例子:
-
@/src/utils/open-window.js
-
@/src/components/SvgIcon/require-icons.js
-
@/src/views/LeafletMap/default-options.js
2. components
与 views
-
所有的
components
文件都是以大写开头PascalCase
,这也是官方所 推荐的。 -
但除了
index.vue
。 -
例子:
@/src/components/BackToTop/index.vue
@/src/components/Charts/Line.vue
@/src/views/LeafletMap/MapContainer/index.vue
components
与views
的区别在于组件本身是否是系统业务组件,views
可理解为各模块页面公用的路由视图组件,component
则是脱离系统业务的公用组件。
3. Pages
-
在
pages
目录下,代表路由的.vue
文件都使用横线连接kebab-case
,代表路由的文件夹也是使用同样的规则。 -
例子:
@/src/pages/data-query/index.vue
@/src/pages/data-query/helper.js
@/src/pages/data-query/components/BarChart.vue
使用 kebab-case
来命名 pages
考虑。
-
横线连接 (kebab-case) 也是官方推荐的命名规范之一 文档
-
pages
下的.vue
文件代表的是一个路由,所以它需要和component
进行区分,component
都是大写开头 -
页面的
url
也都是横线连接的,比如https://www.xxx.com/user-management
,所以路由对应的pages
应该要保持统一 -
没有大小写敏感问题
就近原则
-
components
-
helpers
4. store
、router
、api
-
store
、router
、api
文件遵循pages
目录下的业务分块。 -
store
里面设计到系统全局使用到状态,建议配置成全局getters
。 -
对于
api
目录下的各业务模块都涉及的方法,建议提取成封装成公用模块。
5. helpers
、utils
helpers
与utils
的区别在于是否是系统业务工具方法helpers
可理解为业务相关的方法助手utils
则是脱离业务的可复用的工具。
6. assets
- assets 目录下的
icons
、images
文件夹分别是 svg 目录与静态图片目录, - 目录下的静态资源可以根据业务模块的大小分类放置。
7. themes
themes
系统相关主题目录- 主要是自定义第三方组件库主题样式及组件样式覆写
- 系统 chart 图表主题样式文件等。
搭建 Vue 项目工程化
基于 Vue CLI4 搭建 Vue 2.0 项目
项目生成过程
工程配置
-
Eslint
- ESLint 配置
- ESLint 插件
- ESLint 确保代码上传
-
Prettier
- Prettier 配置
- Prettier 插件
- Prettier 确保代码上传
Babel
.babel.config.js
@vue/cli-plugin-babel/preset
Babel 7
babel-loader
- @vue/babel-preset-app
.browserslistrc
- @babel/preset-env
Webpack
.vue.config.js
- devServer
- publicPath
- productionSourceMap
- loader
- plugins
- optimization.splitChunks
编码规范
- 常规编码规范与技巧
- Vue 编码常识性规范
常规编码规范与技巧
- 团队编码风格
- Front-End Coding Guidelines - 编码规范由京东凹凸实验室整理。
- styleguide - 百度 fex 团队。
- CSS BEM
- CSS 命名 BEM 规范。
- CSS BEM 书写规范。
- 使用 BEM 命名规范来组织 CSS 代码。
- 如何看待 CSS 中 BEM 的命名方式。
- JavaScript
- clean-code-javascript - 作者根据 Robert C. Martin 《代码整洁之道》总结了适用于 JavaScript 的软件工程原则《Clean Code JavaScript》。
- Airbnb javascript - Airbnb javascript 风格指南。
Vue 编码常识性规范
Vue 开发小技巧
一、组件相关
1、生命周期 @hook
- 父组件监听子组件挂载后
mounted
- 一般我们会在每个子组件中去
this.$emit
事件,我们也可以使用@hook
来方便的做这个事情
子组件
export default {
mounted() {
this.$emit('listenMounted')
}
}
父组件
<template>
<div>
<List @listenMounted="listenMounted" />
</div>
</template>
其实还有一种简洁的方法,使用 @hook
即可监听组件生命周期,组件内无需做任何改变。
同样的, created
、 updated
等也可以使用此方法。
<template>
<Child @hook:mounted="childMounted" />
</template>
2、动态注册 hook
-
常见业务场景:
- 使用定时器,生命周期内清除定时器
- 事件监听移除
- 摧毁实例化的对象
一般我们会这样做
export default {
created() {
addEventListener("click", Function, false);
},
beforeDestroy() {
removeEventListener("click", Function, false);
},
};
避免可能遗忘,我们可以这样
export default {
mounted() {
const picker = new Pickaday({
// ...
});
this.$once("hook:beforeDestroy", () => {
picker.destroy();
})
}
};
3、 避免进行数据劫持
Vue
中 data
的数据默认会 Object.defineProperty
对数据进行劫持, 以实现双向数据绑定
示例
export default {
data() {
return {
tableList: [],
};
},
created() {
const tableList = [
{ name: "张四", gender: "male", age: "26" },
{ name: "李三", gender: "fmale", age: "24" },
];
this.tableList = Object.freeze(tableList);
},
};
4、批量数据更新
一次性更新多个数据时
可使用如下方法一起赋值
const {name, age, gender} = resData
this = Object.assign(this, {name, age, gender})
5、依赖注入
示例:
const map = this.$parent.map || this.$parent.$parent.map
问题:
$parent
property- property props
- 不需要数据劫持,避免不必要的性能浪费
provide / inject
<template>
<Map>
<MapTool />
<MapLegend />
</Map>
</template>
父组件注入依赖地图实例方法
export default {
name: "Parent",
provide: () => {
return {
getMap: this.getMap,
};
},
};
子组件接收指定方法
export default {
name: "Child",
inject: ['getMap'],
created () {
console.log(this.getMap())
}
}
在有些业务场景下,我们希望子组件访问父组件较多的方法与熟悉,父组件可以像这样注入组件实例
export default {
name: "Parent",
provide: [this]
};
不过需要注意的是 provide
和 inject
绑定并不是可响应的
6、Vue.observable
我们可以利用这个 Vue.observable( object ) 来应对一些简单的跨组件数据状态共享的情况
my-store.js |
Demo.vue |
---|---|
7、跨组件属性与事件传递
$attrs
$listeners
示例:
<template>
<Child v-bind="$attrs" v-on="$listeners" />
</template>
<script>
import Child from "./Child";
export default {
props: {
title: {
required: true,
type: String
}
}
components: {
Child
}
};
</script>
8、作用域插槽
示例:
<template>
<Promised :promise="usersPromise">
<template v-slot:pending>
<p>Loading...</p>
</template>
<template v-slot="users">
<ul>
<li v-for="user in users">{{ user.name }}</li>
</ul>
</template>
<template v-slot:rejected="error">
<p>Error: {{ error.message }}</p>
</template>
</Promised>
</template>
9、.sync
修饰符
update:myPropName
- 子组件触发
this.$emit('update:modalVisible', false)
- 父组件 property
<ModalPane :modal-visible.sync="visible"></ModalPane>
10、动态组件与动态引入
示例:
<template>
<card>
<menu @on-select="onMenuSelect" />
<component v-bind:is="currentComponent" />
</card>
</template>
<script>
export default {
name: 'Demo',
data() {
return {
currentComponent: ''
}
},
created() {
const currentComponent = 'defaultComponent'
this.onMenuSelect(currentComponent)
},
methods: {
onMenuSelect(component) {
import(`./${component}`).then(module => {
this.currentComponent = module.default
})
}
}
}
</script>
11、watch监听多个变量
示例:
export default {
data() {
return {
msg1: 'apple',
msg2: 'banana'
}
},
compouted: {
msgObj() {
const { msg1, msg2 } = this
return {
msg1,
msg2
}
}
},
watch: {
msgObj: {
handler(newVal, oldVal) {
if (newVal.msg1 != oldVal.msg1) {
console.log('msg1 is change')
}
if (newVal.msg2 != oldVal.msg2) {
console.log('msg2 is change')
}
},
deep: true
}
}
}
二、路由相关
1、使路由组件传参
一般情况会会这么做
export default {
methods: {
getParamsId() {
return this.$route.params.id
}
}
}
使用 props
将组件和路由解耦之后
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true }
]
})
- 这样使用后路由组件可以解耦路由传参
$route.params
的任何参数。
2、按需 keep-alive 与强制刷新路由
router-view
<keep-alive>
<router-view v-if="$route.meta.keepAlive" name="content" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" name="content" :key="$route.fullPath" />
导航加 key
this.$router.push({ name: 'ToRouteName', query: { new: new Date().getTime() } })
优化 vue 性能技巧
常见业务设计与注意问题
- 权限集成
- 路由权限
- 功能权限
- 弹窗封装
- 分页查询列表
- 地图业务