如何建设一个生活服务网站,虚拟电脑可以做网站吗,湖州网站制作,怎么提升网站的排名前言
本文介绍的是有关于vue方面的前端工程化实践#xff0c;主要通过实践操作让开发人员更好的理解整个前端工程化的流程。
本文通过开发准备阶段、开发阶段和开发完成三个阶段开介绍vue前端工程化的整体过程。
准备阶段
准备阶段我将其分为#xff1a;框架选择、规范制…前言
本文介绍的是有关于vue方面的前端工程化实践主要通过实践操作让开发人员更好的理解整个前端工程化的流程。
本文通过开发准备阶段、开发阶段和开发完成三个阶段开介绍vue前端工程化的整体过程。
准备阶段
准备阶段我将其分为框架选择、规范制定、脚手架搭建。
框架选择
vue的框架有很多有脚手架框架例如vue提供脚手架、nuxt等还有vue版本对应的版本UI组件库可选择性更多element、Ant Design、Naive等还有自封的ui组件库。
本文的实践选择的脚手架框架为vue3vitetypescript。
使用的UI组件库为element组件库。
框架选择 脚手架vue3vitetypescript UI组件库element 规范制定
规范制定主要有一下几个点 结构规范
即项目的目录规范每个项目的文件夹命名代表的含义公共组件和页面组件应该存在在哪工具类静态文件应该存放在哪个文件夹应该如何命名等。
1.规范一功能分离结构
作者自己取得名即各个文件夹功能分离。 如上图所示每个文件夹又有其含义每个有特定含义的文件都应该放到对应的文件夹内。
api文件夹
用于存放接口请求的文件夹内部文件命名有两种规则这个是根据作者自身开发经验总结的 1.api文件夹内不允许子文件夹的存在文件名应该以路由名api作为文件名 2.api文件夹内可以有子文件夹子文件夹名称应该与路由文件名同名文件名按照其含义进行命名api切文件夹结构应该只允许存在二级结构 对于第一种规则如下图所示 页面路由home-page下的全部请求应该放在home-page-api文件中。这种规则适合于项目接口较少的项目一般一个页面路由全部请求不超过20个推荐使用这种方式。
对于第二种规则如下图所示 在api文件夹内建一个与页面路由同名的文件夹该文件夹命名规则为页面路由子路由文件夹名api的形式即该子路由内的全部请求方法放到对应的api中的文件内。这种规则适用于路由页面请求方法比较多的情况下使用。
assets文件夹
文件夹要求建于路由页面同名的文件用于存放该路由下的图片资源文件夹不可有三级结构对于组件所需的图片资源应该统一存放在一个统一的文件夹内。 components文件夹
按照组件名命名文件夹 pages-view文件夹
用于存放页面组件pages页面路由入口pages-view存放对应页面。具有共性的页面组件可以统一放到一个文件夹或者提到components文件夹。
例如下图所示红色框表示的是首页路由则绿色框怎就是对应的页面组件关系为路由组件为页面组件提供出口路由组件只做简单props接收和简单逻辑运算 router
用于存放路由配置如果路由过多建议拆分路由。 store
用于存放vuex等配置但是项目中贡献数据不建议使用vuex而是建议存放在缓存中。
utils
用于存放工具类和工具方法
2.规范二模块分离结构
所谓的模块聚合结构则表示的是将api请求页面组件路由配置都存放到view文件夹中router作为主配置引用子路由配置。对于根路径下的components、assets和store作用和规范一的作用一样。 此处下的pages-views、api和router文件夹下的文件名要求不太高只要文件名有特定含义即可但是文件夹下目录不可找过三级。
3.总结 规范一适用于小项目协作人员少的项目结构清晰明了维护方便弊端是协作性比较差。规范二适用于中大型项目协作人员多的项目开发人员只要在自己的目录下开发即可不相互影响提交代码不容易发生冲突。 命名规范
命名规范主要有文件和文件夹命名规范、变量方法计算属性类接口命名规范、css类名命名规范、html组件引用规范导入组件名和组件名规范。
1.文件和文件夹命名规范
文件和文件夹命名全为小写字母多单词之前用横线隔开主要是为了适配window和macwindows不区分大小写mac区分大小写。 2.变量方法类接口命名规范
变量方法计算属性使用小驼峰命名法其中方法末尾需要加Fn计算属性末尾添加Computed(这个是作者个人习惯)。
类接口使用大驼峰命名法其中接口末尾加Types(作者个人习惯用于区分类)。
3.css类名命名规范——BEM
使用BEM命名规范。
BEMBlockElementModifier是一种基于组件的web开发思想。这种开发思想主张将用户界面划分为独立的块。这使得网页开发变得简单快捷即使是复杂的用户界面也可以重用现有的代码而无需复制和粘贴。
Block - 一个功能独立的页面组件可以重用。
block名称描述了此模块的用途它是什么各个模块可以相互嵌套嵌套层级数量不受限
例如
!-- header block --
header classheader!-- Nested logo block --div classlogo/div!-- Nested search-form block --form classsearch-form/form
/header
header 模块嵌套了logo模块和搜索表单模块
Element - 块的组成模块不能与块分开使用也不能自己单独使用。
element名称描述了在这模块中的用途它是什么element名称的语法结构为 **block-name__element-name**使用双下划线将block名称和element名称连接起来。element 元素彼此之间可以相互嵌套嵌套层级数量不受限一个element元素里可以嵌套包含一个block块这就意味着element名称定义不能为多层级结构如 block__elem1__elem2 这种命名是不被允许的。
例如
form classsearch-formdiv classsearch-form__contentinput classsearch-form__inputbutton classsearch-form__buttonSearch/button/div
/form
search-forminput,search-formbutton,search-form__content 即为element元素。
search-form__content 元素中嵌套了element元素。
但是search-form__content__input 这种多层级命名element元素是不被允许的类名过长层级结构过多不清晰。
Modifier - 定义block块或element元素的外观、状态或行为。
modifier 的名称一般描述了block块或element元素的外观它的大小它的状态它的颜色modifier 的名称语法结构为block-name_modifier-nameblock-name__element-name_modifier-name一般使用单下划线将它跟block元素或者element元素连接起来布尔形式区分是或不是的状态完整的语法结构为block-name_modifier-nameblock-name__element-name_modifier-name
例如
form classsearch-form search-form_theme_islandsinput classsearch-form__input!-- The button element has the size modifier with the value m --button classsearch-form__button search-form__button_disabledSearch/button
/form
search-form__button_disabled 这种命名结构是布尔形式。
key-value键值对的形式区分不同的状态。完整的语法结构则为
block-name_modifier-name_modifier-valueblock-name__element-name_modifier-name_modifier-value
例如
form classsearch-form search-form_theme_islandsinput classsearch-form__input!-- The button element has the size modifier with the value m --button classsearch-form__button search-form__button_size_mSearch/button
/form
search-form__button_size_m 这种命名结构就是键值对的形式
modifier 不能被单独使用必须与block元素或者element元素联合使用。因为一个modifier就是用来描述此元素的外观、大小、一个实体的状态。 BEM的优点与缺点
优点
结构简单一目了然组件化代码复用不使用标签选择器避免父级元素内的标签的受影响。举个例子商品详情页是允许商家自定义标签的那么商家展示区域标签的祖先元素一旦用标签选择器定义了样式子子孙孙都要背负.
例如将这个网页拆分成BEM的写法
无BEM写法
sectionh1Sterling Calculator/h1form actionprocess.php methodpostpPlease enter an amount: (e.g. 92p, pound;2.12)/ppinput nameamount input typesubmit valueCalculate/p/form
/section
BEM 写法
section classwidgeth1 classwidget__headerSterling Calculator/h1form classwidget__form actionprocess.php methodpostpPlease enter an amount: (e.g. 92p, pound;2.12)/ppinput nameamount classwidget__input widget__input_amount input typesubmit valueCalculate classwidget__input widget__input_submit/p/form
/section
元素清单
widgetwidget__headerwidget__formwidget__input
这样就形成了一个可复用的块
注意其中的 widget__input_amount 和 widget__input_submit为Modifier
缺点
类名变的更长一个元素可能拥有多个classid选择器无用武之地class命名不能重复Block的抽象至关重要
谁适用于BEM
项目复杂复用模块较多,多人协作团队使用。
4.html组件引入规范导入组件名和组件名规范
html组件引入的组件名称使用大驼峰带结束标签 导入组件名使用大驼峰 import TipsDialog from ../tips-dialog/index.vue 组件名规范使用大驼峰 defineOptions({ name: CompModel }) 代码规范
代码规范主要是代码校验和自动格式化以及git提交规范校验。
1.代码校验和自动格式化
代码校验和代码格式化两个是相辅相成的通过插件对代码进行校验并自动格式化成符合要求的格式。要实现需要依赖两个插件eslint和prettier。
配置eslint
下载依赖 yarn add -D eslint eslint-plugin-vue npx eslint --init init 命令会自动生成 .eslintrc.js 修改配置为
module.exports {root: true,ignorePatterns: [node_moduls/*],env: {browser: true,es2021: true,node: true,commonjs: true},extends: [eslint:recommended, plugin:vue/essential, prettier],overrides: [],parserOptions: {ecmaVersion: latest,sourceType: module},plugins: [vue, prettier],rules: {linebreak-style: [error, unix],no-multiple-empty-lines: [1, { max: 2 }], //空行最多不能超过2行vue/multi-word-component-names: off //vue组件名去掉多单词限制}
}
配置文档
规则 | Rules_Eslint_参考手册_非常教程 (verydoc.net)
配置代码风格工具prettier
安装 yarn add -D prettier eslint-config-prettier eslint-plugin-prettier 创建 .prettierrc
{useTabs: false,tabWidth: 4,printWidth: 80,singleQuote: true,trailingComma: none,semi: false,endOfLine: lf
}
官方地址
Configuration File · Prettier 中文网
2.git提交规范
相关配置在这篇文章内容有点多
git提交规范-CSDN博客
api和数据规范
这个部分需要后端配置后端返回的code需要代表一定含义这样才能做统一数据处理例如token失效的code为多少接口超时的code或者接口报错的返回code为多少。
1.api封装
这个部分比较灵活需要按照公司的业务或者开发的个人习惯进行封装以下是一个例子
import axios, { AxiosRequestConfig } from axios
import qs from qs
import { getToken } from ./userInfo/token
import { subscribe, MetaDataHelper } from gogoal/fund-utils
import { blob2File } from ./tools
import { isFileList, isObject } from ./typesconst SUCCESS_CODE 0
const service axios.create({// timeout: 200000, // request timeout
})
// response interceptor
service.interceptors.response.use((response) {const res response.dataif (res.code SUCCESS_CODE) {return res.data} else {subscribe.notify(xhr-fail, res)console.log(xhr-fail response, response)return Promise.reject(res)// if (res.code 1100 || res.code 1103) {// dealLogout()// } else {// return Promise.reject(res)// }}},(error) {return Promise.reject({code: 1,message: 服务器错误,error})}
)interface RequestDataParams extends AxiosRequestConfig {needToken?: boolean /* 是否需要token */needToString?: boolean /* 是否需要将参数转成token */data?: Recordstring, anyheaders?: Recordstring, anyconfig?: Recordstring, any
}
// 请求接口数据
// interface ResponseDataT any {
// code: number
// data: T
// message: string
// }const getData function T({url ,params {},data,method GET,needToken false,needToString false,headers,config
}: RequestDataParams): PromiseT {if (needToken) {const token getToken()params.token token}const _params {url,method}// deleteEmptyProperty(params);if ([POST, PUT, PATCH].includes(method.toUpperCase())) {Object.assign(_params, {data: needToString ? qs.stringify(data) : data,params: params})} else {Object.assign(_params, {params: params})}if (headers) {Object.assign(_params, {headers})}if (config) {Object.assign(_params, {config})}return service(_params)
}/*** description 公用 GET 请求*/
const get function T({url,params,needToken false,needToString false
}: RequestDataParams) {return getDataT({url,params,needToken,method: GET,needToString})
}/*** description 公用 POST 请求*/
const post function T({url,params,data,needToken false,needToString false
}: RequestDataParams) {return getDataT({url,params,data,needToken,method: POST,needToString})
}
const upload function ({ url, data, params {}, needToken false }) {// debuggerconst _url url || /file/comm_upload_fileconst _data new FormData()// 将得到的文件流添加到FormData对象Object.keys(data).forEach((_k) {const _p data[_k]if (isObject(_p)) {_data.append(_k, JSON.stringify(_p))} else if (isFileList(_p)) {Array.from(_p).forEach((res) {_data.append(_k, res)})} else if (isArray(_p)) {_p.forEach((res) {getType(res) File _data.append(_k, res)})} else {_data.append(_k, _p)}})return getData({url: _url,method: POST,data: _data,params,needToken})
}function formatFile(query) {const _params new FormData()// 将得到的文件流添加到FormData对象Object.keys(query).forEach((_k) {const _p query[_k]if (isObject(_p)) {// _params.append(_k, JSON.stringify(_p))_params.append(_k, _p)} else if (isFileList(_p)) {_p.forEach((res) {_params.append(_k, res)})} else {_params.append(_k, _p)}})return _params
}
const downloadBlob function (url, fileName) {const xhr new XMLHttpRequest()xhr.open(get, url, true)xhr.responseType blob // 返回类型blob// 定义请求完成的处理函数请求前也可以增加加载框/禁用下载按钮逻辑xhr.onload function () {// 请求完成if (this.status 200) {// 返回200const blob this.responseblob2File(blob, fileName)}}// 发送ajax请求xhr.send()
}type File {url: stringquery?: stringparams?: stringfileName: stringconfig: any
}
const downloadFile ({ url, query, params, fileName, config {} }: File) {const _config: AxiosRequestConfig Object.assign({method: post,headers: {Content-Type: application/x-www-form-urlencoded // 请求的数据类型为form data格式},responseType: blob},config)let _url url || /file_export/excel/commonif (query) {_url ?${qs.stringify(query)}}_config.url _url_config.headers[X-Pro] MetaDataHelper.getId()if (params) {_config.data qs.stringify(params)}return new Promisevoid((resolve, reject) {axios(_config as AxiosRequestConfig).then((response) {const { data, message } responseif (!data message) {reject(response)return}blob2File(data, fileName)resolve()}).catch((error) {reject(error)})})
}export default getDataexport {service /* 导出方便修改默认参数 */,get,post,upload,downloadFile,downloadBlob,formatFile
}使用
const response await service({url: /api/v1/zyfp_account_home/bind_mobile,method: post,data: {source: login,mobile: bindForm.value.mobile,sms_code: bindForm.value.sms_code}})
2.数据规范
这个需要后端定主要是接口返回的数据格式例如返回code规范返回对象规范等这个需要根据具体情况具体分析。
脚手架搭建