多端开发项目规范
背景:
为了兼容多端(H5+小程序)开发,我们用 Taro 这个框架来搭建(H5+小程序)端的规范
项目的目录结构
|-- babel.config.js
|-- config
| |-- dev.js
| |-- index.js
| -- prod.js
|-- dist
|-- global.d.ts
|-- package.json
|-- project.config.json
|-- src
| |-- Bus.js
| |-- api
| | |-- rquest.js
| | |-- utils.js
| |-- app.config.ts
| |-- app.scss
| |-- app.ts
| |-- components
| | |-- navBar
| | |-- index.vue
| | |-- config.js
| | |-- Header.vue
| |-- index.html
| |-- pages
| | |-- index
| | | |-- index.config.ts
| | | |-- index.scss
| | | |-- index.vue
| | |-- my
| | | |-- my.config.ts
| | | |-- my.scss
| | | |-- my.vue
| | |-- propValue
| | |-- propValue.config.ts
| | |-- propValue.scss
| | |-- propValue.vue
| |-- static
| | |-- css
| | |-- constant.scss
| | |-- public.scss
| |-- store
| |-- store.js
|-- tsconfig.json
组件
什么情况要拆分组件?
组件拆分的原则是:三个或以上页面出现该组件,或者 两个页面且功能复杂的即可拆分。对于复杂页面,可以按功能区块来拆分组件
组件的命名规范
组件可以根据模块功能来命名,如果组件有多个文件,则新建一个文件夹,命名为驼峰的形式,它下面的组件名是 index.vue。如果是单个文件,组件名首字母为大写如:Header.vue
|-- src
| |-- components
| | |-- navBar
| | |-- index.vue
| | |-- config.js
| | |-- Header.vue
组件间的通信方式
父组件传子组件值
用 props 传值:父子 prop 之间形成了一个单向下行绑定,父级 prop 的更新会向下流动到子组件中
父组件的代码
<Deerma-button
prop-text="父组件传过来的值"
class="themColor"
@updateText="updateText"/>
子组件的代码
export default{
props: {
propText:{
type:String
}
}
}
子组件向父组件传值
用 emit 方法:子组件可以使用 $emit 触发父组件的自定义事件,同时将值传过去,从而改变父组件的值
子组件代码
this.$emit('updateText','子组件传过去的值')
父组件代码
<Deerma-button
class="themColor"
@updateText="updateText"
/>
非父子之间传值
1.通过 vuex 传值,可以参考官网(https://taro-docs.jd.com/taro/docs/guide/)
store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
thread: {}
},
mutations: {
setThread: (state, thread) => {
state.thread = { ...thread }
}
}
})
export default store
B组件
this.$store.commit('setThread',{name:'李小姐'})
A组件:获取B组件传来的值
this.name=this.$store.state.thread
2.通过event bus 传值
新建文件 Bus.js
import Vue from 'vue'
const bus = new Vue()
export default bus
A组件
<template>
<div>
<text>{{ name }}</text>
<text @tap="propVal">传值</text>
</div>
</template>
<script>
import Bus from '../../Bus.js' //Bus.js的路径
export default {
data () {
return {
name: '张三'
}
},
methods: {
propVal(){
Bus.$emit('val',this.name)
}
},
beforeDestroy () {
Bus.$off('val') //页面销毁之前销毁 eventBus 防止其他页面也触发了该页面的事件
}
}
</script>
B组件:获取A组件传来的值
<template>
<div>
<text>{{ propVal }}</text>
</div>
</template>
<script>
import Bus from '../../Bus.js'
export default {
data () {
return {
propVal:'李四'
}
},
created() {
Bus.$on('val', (data) => {
this.propVal=data
})
}
}
</script>
路由
路由跳转方式
1.保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面 Taro.navigateTo({url: 'test'})
2.跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 Taro.switchTab({url: '/index'})
3.关闭所有页面,打开到应用内的某个页面 Taro.reLaunch({url: 'test?id=1'})
4.关闭当前页面,跳转到应用内的某个页面。不允许跳转到 tabbar 页面 Taro.redirectTo({url: 'test'})
5.关闭当前页面,返回上一页面或多级页面 Taro.navigateBack({delta: 2})
路由命名方式
以驼峰的形式命名 如:propValue
src
|-- Bus.js
|-- pages
| |-- index
| | |-- index.config.ts
| | |-- index.scss
| | |-- index.vue
| |-- propValue
| |-- propValue.config.ts
| |-- propValue.scss
| |-- propValue.vue
样式
样式命名方式
全局样式
全局样式命名: common+作用名 如:common-font-color
组件样式
最大的 view 的 class 名字为: 模块名+Box 如:application-box
<template>
<view class='add-application-box'>
<text>内容区块</text>
</view>
</template>
页面样式
最大的 view 的 class 名字为: 页面名+Box 如:my-slef
<template>
<view class='my-slef'>
<text>内容区块</text>
</view>
</template>
页面的公共样式:公共变量,宏,函数
全局引入样式:在项目更目录中找到 config/dev.js 中写入下面代码:
const path = require('path')
module.exports = {
sass: {
resource: path.resolve(__dirname, '..', 'src/static/css/constant.scss')
}
}
公共变量
//公共变量应该放在 src/static/css/constant.scss 里面,代码如下:
$font-size:25px;
//在其他的scss文件中使用
.text{
font-size: $font-size;
}
宏
//宏应该放在 src/static/css/constant.scss 里面,代码如下:
@mixin rounded-corners {
-moz-border-radius: 30px;
-webkit-border-radius: 30px;
border-radius: 30px;
}
//在其他的scss文件中使用
.text{
@include rounded-corners;
}
函数
//公共变量应该放在 src/static/css/constant.scss 里面,代码如下:
@mixin link-colors($normal, $hover, $visited) {
color: $normal;
&:hover { color: $hover; }
&:visited { color: $visited; }
}
//在其他的scss文件中使用
.text{
@include link-colors(blue, red, green)
}
eslint的配置
{
"sublimeTextKeymap.promptV3Features": true,
"javascript.implicitProjectConfig.experimentalDecorators": true,
"editor.multiCursorModifier": "ctrlCmd",
"editor.snippetSuggestions": "top",
"editor.formatOnPaste": false,
"editor.tabSize": 2,
"editor.fontSize": 20,
"editor.formatOnSave": false,
"editor.acceptSuggestionOnEnter": "off",
"window.zoomLevel": 0,
"editor.renderWhitespace": "none",
"editor.renderControlCharacters": true,
"editor.minimap.enabled": true,
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/tmp": true,
"**/node_modules": true,
"**/bower_components": true
},
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/**": true,
"**/tmp/**": true,
"**/bower_components/**": true
},
"workbench.colorCustomizations": {
"tab.activeBackground": "#be0a4c"
},
"explorer.autoReveal": false,
"explorer.confirmDelete": false,
// eslint
"eslint.validate": [
"html",
{
"language": "vue",
"autoFix": true
},
{
"language": "javascript",
"autoFix": true
},
{
"language": "javascriptreact",
"autoFix": true
},
{
"language": "typescript",
"autoFix": true
},
{
"language": "json",
"autoFix": true
}
],
"eslint.options": {},
// vetur
"vetur.format.defaultFormatter.html": "js-beautify-html",
"vetur.format.defaultFormatterOptions": {
"wrap_attributes": "force-aligned"
},
"vetur.format.defaultFormatter.js": "prettier-eslint",
"vetur.experimental.templateInterpolationService": false,
"workbench.startupEditor": "welcomePage",
"editor.accessibilitySupport": "off",
"explorer.confirmDragAndDrop": false,
"files.associations": {
"*.cjson": "jsonc",
"*.wxss": "css",
"*.wxs": "javascript",
"*.wpy": "vue"
},
"emmet.includeLanguages": {
"wxml": "html"
},
"minapp-vscode.disableAutoConfig": true,
"javascript.updateImportsOnFileMove.enabled": "always",
"javascript.format.enable": false,
"prettier.semi": false,
"prettier.jsxSingleQuote": true,
"prettier.singleQuote": true,
"prettier.eslintIntegration": true,
"breadcrumbs.enabled": true,
"prettier.printWidth": 240,
"eslint.autoFixOnSave": false,
"scss.lint.duplicateProperties": "warning",
"scss.lint.zeroUnits": "warning",
"fileheader.Author": "lu",
"fileheader.LastModifiedBy": "lu",
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"sync.gist": "e25f5b82bc1db4aad79a24ca77e34b7c",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"dart.flutterSdkPath": "/Users/apple/D/flutter/flutter",
"todo-tree.tree.showScanModeButton": false,
"editor.rulers": [],
"hediet.vscode-drawio.local-storage": "eyIuZHJhd2lvLWNvbmZpZyI6IntcImxhbmd1YWdlXCI6XCJcIixcImN1c3RvbUZvbnRzXCI6W10sXCJsaWJyYXJpZXNcIjpcImdlbmVyYWxcIixcImN1c3RvbUxpYnJhcmllc1wiOltcIkwuc2NyYXRjaHBhZFwiXSxcInBsdWdpbnNcIjpbXSxcInJlY2VudENvbG9yc1wiOltdLFwiZm9ybWF0V2lkdGhcIjpcIjI0MFwiLFwiY3JlYXRlVGFyZ2V0XCI6ZmFsc2UsXCJwYWdlRm9ybWF0XCI6e1wieFwiOjAsXCJ5XCI6MCxcIndpZHRoXCI6ODI3LFwiaGVpZ2h0XCI6MTE2OX0sXCJzZWFyY2hcIjp0cnVlLFwic2hvd1N0YXJ0U2NyZWVuXCI6dHJ1ZSxcImdyaWRDb2xvclwiOlwiI2QwZDBkMFwiLFwiZGFya0dyaWRDb2xvclwiOlwiIzZlNmU2ZVwiLFwiYXV0b3NhdmVcIjp0cnVlLFwicmVzaXplSW1hZ2VzXCI6bnVsbCxcIm9wZW5Db3VudGVyXCI6MCxcInZlcnNpb25cIjoxOCxcInVuaXRcIjoxLFwiaXNSdWxlck9uXCI6ZmFsc2UsXCJ1aVwiOlwiXCJ9In0=",
"terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe",
"eslint.codeAction.showDocumentation": {
"enable": true
}
}