美文网首页
我理解的项目规范

我理解的项目规范

作者: 人猿Jim | 来源:发表于2021-12-07 18:19 被阅读0次
  • 结构规范
    对项目进行分层,比如网络层,组件层,路由层,工具层,存储层,他们表现为一个个具体的文件夹,其中文件夹的名字对应什么层是一目了然的。
    比如网络层,将具体的请求方法封装到http,而数据接口封装到service,请求到的数据保存到VueX或者Rudex中。

    网络层
  • 代码规范
    Eslint:插件方式的代码规范管理工具。值得注意的是eslint不仅可以规范普通的es规范,还能做到Vue组件的规范,比如生命周期顺序和指令等

// .eslintrc.js
module.exports = {
  root: true,

  env: {
    browser: true,
    node: true,
    es6: true
  },

  extends: [
    // 这个需要安装依赖 npm i @vue/eslint-config-standard  eslint-plugin-vue -D
    'plugin:vue/essential',
    '@vue/standard'
  ],

  plugins: [
    'vue'
  ],

  rules: {
    // 'vue/no-parsing-error':'off',
    // no-console error 代表存在console就会报错, off 代表时关闭,on代表时开启
    'no-console': 0,
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'accessor-pairs': 2,
    'arrow-spacing': [2, {
      before: true,
      after: true
    }],
    'block-spacing': [2, 'always'],
    'brace-style': [2, '1tbs', {
      allowSingleLine: true
    }],
    camelcase: [0, {
      properties: 'always'
    }],
    'comma-dangle': [2, 'never'],
    'comma-spacing': [2, {
      before: false,
      after: true
    }],
    'comma-style': [2, 'last'],
    'constructor-super': 2,
    curly: [2, 'multi-line'],
    'dot-location': [2, 'property'],
    'eol-last': 2,
    eqeqeq: [2, 'allow-null'],
    'generator-star-spacing': [2, {
      before: true,
      after: true
    }],
    'handle-callback-err': [2, '^(err|error)$'],
    indent: [2, 2, {
      SwitchCase: 1
    }],
    'jsx-quotes': [2, 'prefer-single'],
    'key-spacing': [2, {
      beforeColon: false,
      afterColon: true
    }],
    'keyword-spacing': [2, {
      before: true,
      after: true
    }],
    'new-cap': [2, {
      newIsCap: true,
      capIsNew: false
    }],
    'new-parens': 2,
    'no-array-constructor': 2,
    'no-caller': 2,
    // 'no-console': 'off',
    'no-class-assign': 2,
    'no-cond-assign': 2,
    'no-const-assign': 2,
    'no-control-regex': 0,
    'no-delete-var': 2,
    'no-dupe-args': 2,
    'no-dupe-class-members': 2,
    'no-dupe-keys': 2,
    'no-duplicate-case': 2,
    'no-empty-character-class': 2,
    'no-empty-pattern': 2,
    'no-eval': 2,
    'no-ex-assign': 2,
    'no-extend-native': 2,
    'no-extra-bind': 2,
    'no-extra-boolean-cast': 2,
    'no-extra-parens': [2, 'functions'],
    'no-fallthrough': 2,
    'no-floating-decimal': 2,
    'no-func-assign': 2,
    'no-implied-eval': 2,
    'no-inner-declarations': [2, 'functions'],
    'no-invalid-regexp': 2,
    'no-irregular-whitespace': 2,
    'no-iterator': 2,
    'no-label-var': 2,
    'no-labels': [2, {
      allowLoop: false,
      allowSwitch: false
    }],
    'no-lone-blocks': 2,
    'no-mixed-spaces-and-tabs': 2,
    'no-multi-spaces': 2,
    'no-multi-str': 2,
    'no-multiple-empty-lines': [2, {
      max: 1
    }],
    'no-native-reassign': 2,
    'no-negated-in-lhs': 2,
    'no-new-object': 2,
    'no-new-require': 2,
    'no-new-symbol': 2,
    'no-new-wrappers': 2,
    'no-obj-calls': 2,
    'no-octal': 2,
    'no-octal-escape': 2,
    'no-path-concat': 2,
    'no-proto': 2,
    'no-redeclare': 2,
    'no-regex-spaces': 2,
    'no-return-assign': [2, 'except-parens'],
    'no-self-assign': 2,
    'no-self-compare': 2,
    'no-sequences': 2,
    'no-shadow-restricted-names': 2,
    'no-spaced-func': 2,
    'no-sparse-arrays': 2,
    'no-this-before-super': 2,
    'no-throw-literal': 2,
    'no-trailing-spaces': 2,
    'no-undef': 2,
    'no-undef-init': 2,
    'no-unexpected-multiline': 2,
    'no-unmodified-loop-condition': 2,
    'no-unneeded-ternary': [2, {
      defaultAssignment: false
    }],
    'no-unreachable': 2,
    'no-unsafe-finally': 2,
    'no-unused-vars': [2, {
      vars: 'all',
      args: 'none'
    }],
    'no-useless-call': 2,
    'no-useless-computed-key': 2,
    'no-useless-constructor': 2,
    'no-useless-escape': 0,
    'no-whitespace-before-property': 2,
    'no-with': 2,
    'one-var': [2, {
      initialized: 'never'
    }],
    'operator-linebreak': [2, 'after', {
      overrides: {
        '?': 'before',
        ':': 'before'
      }
    }],
    'padded-blocks': [2, 'never'],
    quotes: [2, 'single', {
      avoidEscape: true,
      allowTemplateLiterals: true
    }],
    semi: [2, 'never'],
    'semi-spacing': [2, {
      before: false,
      after: true
    }],
    'space-before-blocks': [2, 'always'],
    'space-before-function-paren': [2, 'never'],
    'space-in-parens': [2, 'never'],
    'space-infix-ops': 2,
    'space-unary-ops': [2, {
      words: true,
      nonwords: false
    }],
    'spaced-comment': [2, 'always', {
      markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
    }],
    'template-curly-spacing': [2, 'never'],
    'use-isnan': 2,
    'valid-typeof': 2,
    'wrap-iife': [2, 'any'],
    'yield-star-spacing': [2, 'both'],
    yoda: [2, 'never'],
    'prefer-const': 2,
    // 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
    'object-curly-spacing': [2, 'always', {
      objectsInObjects: false
    }],
    'array-bracket-spacing': [2, 'never'],
    // 追加vue的编码规范
    "vue/max-attributes-per-line": ["error", { // 标签的属性必须在单独一行
      "singleline": 1,
      "multiline": {
        "max": 1,
        "allowFirstLine": false
      }
    }], 
    "vue/order-in-components": ["error", { // 规定组件的属性顺序
      "order": [
        "el",
        "name",
        "parent",
        "functional",
        ["delimiters", "comments"],
        ["components", "directives", "filters"],
        "extends",
        "mixins",
        "inheritAttrs",
        "model",
        ["props", "propsData"],
        "data",
        "computed",
        "watch",
        "LIFECYCLE_HOOKS",
        "methods",
        ["template", "render"],
        "renderError"
      ]
    }],
    "vue/v-bind-style": ["error", "shorthand"], // v-bind: 和 : 统一使用:
    "vue/v-on-style": ["error", "shorthand"], // v-on 和 @ 统一使用@
    "vue/name-property-casing": ["error", "PascalCase"], // 组件明大写
    "vue/component-name-in-template-casing": ["error", "kebab-case", {// 模板的组件使用小写加-
      "registeredComponentsOnly": false,
      "ignores": []
    }],
    "vue/this-in-template": ["error", "never"], // 禁止templent上使用this
    "vue/attributes-order": ["error", { // 指定标签上属性的顺序
      "order": [
        "DEFINITION",
        "LIST_RENDERING",
        "CONDITIONALS", 
        "RENDER_MODIFIERS",
        "GLOBAL", 
        "UNIQUE", 
        "TWO_WAY_BINDING", 
        "OTHER_DIRECTIVES", 
        "OTHER_ATTR", 
        "EVENTS", 
        "CONTENT"
      ]
    }],
    "vue/require-default-prop": ["error"], // 定义的属性必须要给默认属性
    "vue/prop-name-casing": ["error", "camelCase"], // 属性的名称的命名方式
    
  },

  parserOptions: {
    parser: 'babel-eslint'
  },

  overrides: [
    {
      files: [
        '**/__tests__/*.{j,t}s?(x)',
        '**/tests/unit/**/*.spec.{j,t}s?(x)'
      ],
      env: {
        jest: true
      }
    }
  ]
}

  • 项目文档
    对于一般的项目,一般有个README就够了,比较大型的项目会用到自己封装的框架,而这个框架最好内嵌一个文档。
  • 组件规范
    组件规范一部分可以在eslint上实现,还有一部分是需要结合官方提供的API,比如有时候在复用同一个子组件时,会想到 $attrs 和 $listener,但这样会造成信息的遗漏,不能一眼看出调用了子组件哪些方法,这个时候直接把事件和属性写上去会更直观。
  • 工程自动化
    项目中总会有些许机械化的工作,比如modules的导入导出,路由的导入导出,这些机械性的工作往往可以通过工程自动化来实现,比如VueX中讲modules统一地进行导入。
// load-modules.js
/**
 * 自动加载store的二级 modules
 */
const modules = {}
// 需要将module名命名为index.js才能匹配到
const allModules = require.context('@/store/modules/', true, /index\.(js|ts)$/)
// ["./login/index.js", "./main/books/index.js", "./main/goods/index.js", "./register/index.js"]
console.log('allModules=', allModules.keys())
allModules.keys().forEach((item, index, array) => {
  // item = ./login/index.js => login/index.js
  // item = ./main/books/index.js => main/books/index.js
  const module_path = item.substr(2)
  const moduleNames = module_path.split('/') // [login, index.js] - [main, books, index.js]
  moduleNames.pop()
  const key = moduleNames.join('_')

  // const module = require(`@/store/modules/${module_path}`)
  const module = allModules(item)
  modules[key] = module.default
})
/**
 * modules:{
    goods :{
         // 0.启用命名空间
         namespaced: true,
         // 1.定义状态
         state: {
           data: {}, // 列表数据
           recordDetail: {} // 详情数据
         },
         // 2.修改状态
         mutations: {
           // 这里的 `state` 对象是模块的局部状态
           [Types.addData](state, payload) {
             state.data = payload
           },
           [Types.recordDetail](state, payload) {
             state.recordDetail = payload
           }
         },
         // 3.提交action,来修改状态
         actions: {
           async list(context, payload) {
             // context 对象 与 store对象有相同的方法;context != store
             // 注意:局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
             const config = {
               headers: {
                 'Content-Type': 'application/json;charset=utf-8'
               }
             }
             try {
               const result = await interrogaterecordService.list(payload, config)
               console.log('result=', result)
               context.commit(Types.addData, result.data.data)
               return Promise.resolve(result.data.data)
             } catch (err) {

             }
           },
           async recordDetail(context, payload) {
             // context 对象 与 store对象有相同的方法;context != store
             // 注意:局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
             const config = {
             }
             try {
               const result = await interrogaterecordService.recordDetail(payload, config)
               Console.log('recordDetail=', result)
               context.commit(Types.recordDetail, result.data.data)
               return Promise.resolve(result.data)
             } catch (err) {

             }
           }
         },
         // 4.获取定义的状态, 通过store.getters获取里面的函数,例如:store.getters.count
         getters: {
           // state 是获取局部状态;rootState是获取根状态
           ...ComGetters, // list --> data.content ; listPageConfig -> pageConfig
           recordDetail(state, getters, rootState, rootGetters) {
             return state.recordDetail || {}
           }
         }
       }
 * }
 */
export default modules || {}

路由模块的工程化导出,这需要项目规范化的配合,即每个页面文件夹下都对应存放一个路由文件,最后通过load-routes.js统一进行导出,形成一个完整routes对象。


路由导入
// import Login from './login.vue'
const Login = () => import(/* webpackChunkName: "login" */ './login.vue')
export default {
  path: '/login',
  name: 'login',
  pname: '', // 父亲路由的名称
  level: 1, // 一级路由
  component: Login,
  children: [
    // {
    //   path: 'goodslist', // /main/goodslist
    //   name: 'goodslist',
    //   component: GoodsList,
    //   children: [
    //     {
    //       path: 'create', // /main/goodslist/create
    //       name: 'create',
    //       component: GoodsListCreate
    //     }
    //   ]
    // }
  ],
  meta: {
    keepAlive: false, // 不需要缓存
    requireAuth: false // 不需要登录才能访问
  }
}

// load-routes.js
const routes = []
// allRoute 是一个函数
const allRoute = require.context('@/views/', true, /route\.(js|ts)$/)
// ["./login/route.js", "./main/goods/list/route.js", "./main/route.js", "./no-find/route.js", "./register/route.js"]
// console.log('allRoute=', allRoute.keys())
allRoute.keys().forEach((file_path, index, array) => {
  const route = allRoute(file_path)
  routes.push(route.default) // router.default 拿到的才是导出的对象,router是模块对象
})
/**
 * routes:[
    {
      path: '/main',
      name: 'main',
      level: 1,
      pname:'',
      component: Main,
      children: [
      ]
    },
    {
      path: '/login',
      name: 'login',
      level: 1,
      pname:'',
      component: Login,
      children:[

      ]
    },
    {
      children: []
      component: {name: "List", components: {…}, staticRenderFns: Array(0), _compiled: true, render: ƒ, …}
      level: 2
      pname:'main',
      name: "goodslist"
      path: "goodslist"
      pname: "main"
    }
 * ]
 */
export default routes || []

通过register-routes.js进行路由整合

// register-routes.js
import AllRoute from '@/views/load-routes.js'
// 获取一级路由
const aRoutes = AllRoute.filter((route) => {
  return route.level === 1
})
// 获取一级路由
const bRoutes = AllRoute.filter((route) => {
  return route.level === 2
})
// 获取一级路由
const cRoutes = AllRoute.filter((route) => {
  return route.level === 3
})

aRoutes.forEach(element1 => {
  // 遍历2级路由
  bRoutes.forEach((element2) => {
    if (element2.pname === element1.name) {
      // 遍历3级路由
      cRoutes.forEach((element3) => {
        if (element3.pname === element2.name) {
          element2.children.push(element3)
        }
      })
      element1.children.push(element2)
    }
  })
})
// console.log(aRoutes)
export default aRoutes
  • 服务器/持续集成规范
    这一块运用的技术比较多,比如Docker可以生成一个统一的nginx镜像,里面include各个项目的.conf文件
    再者可以用Jenkins或者Gulp搭载一个前端自动化发布的工具
// gulpfile.js
// 在Node中不能使用ES6语法的模块化
const gulpConfig = require('./gulpfile.config.js')

const gulp = require('gulp')
const GulpSSH = require('gulp-ssh')
// 需要上传到服务器的路径
const config = gulpConfig.devServerSShConfig.sshConfig
// const config = systemConfig.proServerSShConfig.sshConfig;

const gulpSSH = new GulpSSH({
  ignoreErrors: false,
  sshConfig: config.ssh
})

/**
 * 1.备份:tar -zcvf  备份后文件的存储路径和名称  这里*代表压缩该命令所在文件夹下的所有内容
 *   备份时最好要进入到指定的文件夹在开始压缩,这样压缩后的文件解压不会带路劲目录
 *
 *   备份的文件夹必须已经是存在,才能进行备份
 */
gulp.task('execSSHBackup', () => {
  console.log('备份服务器上现有文件...')
  return gulpSSH.shell(config.backups, { filePath: 'log/commands-backup.log' })
    .pipe(gulp.dest('logs'))
})

/**
 * 2.解压: tar -zxvf  将这个文件夹下的压缩文件  解压到这个目录下
 * 执行这个脚本需要手动修改config/index 里面的historyProjectName属性,例如:2019-4-17-20,指定回滚到这个版本
*/
gulp.task('execSSHRollBack', () => {
  console.log('回滚上一个版本...')
  return gulpSSH.shell(config.rollback, { filePath: 'log/commands-unZip.log' })
    .pipe(gulp.dest('logs')) // 会自动新建该目录
})

gulp.task('reloadNginx', () => {
  console.log('重启服务器...')
  return gulpSSH.shell(config.reload, { filePath: 'log/commands-reloadNginx.log' })
    .pipe(gulp.dest('logs'))
})

/**
 *上传前先删除服务器上现有文件...
*/
gulp.task('execSSHDelete', () => {
  console.log('删除服务器上现有文件...')
  return gulpSSH.shell(config.commands, { filePath: 'log/commands-delete.log' })
    .pipe(gulp.dest('logs'))
})

/**
 * publish 发布代码
 */
gulp.task('publish', () => {
  console.log('开始上传文件到服务器...')
  return gulp.src([gulpConfig.devServerSShConfig.uploadFile])
    .pipe(gulpSSH.dest(config.remotePath))
})

/**
 * gulp自动化部署。gulp.series:按照顺序执行
 * 删除,发布,备份,重启
 * 'execSSHDelete', 'publish', 'execSSHBackup', 'reloadNginx'
 */
gulp.task('default', gulp.series('execSSHDelete', 'publish', 'execSSHBackup', 'reloadNginx', (done) => {
  console.log('发布完毕...', 'http://' + config.ssh.host + ':9090')
  // Did you forget to signal async completion? 报错后需要调用done,以结束task
  done() // 在不使用文件流的情况下,向task的函数里传入一个名叫done的回调函数,以结束task
}))

// gulpfile.config.js
/**
* 远程服务器的配置文件
* "gulp": "4.0.0",
* "gulp-ssh": "0.7.0",
*
* 测试服务器:196.168.0.124
* root/keduoli
*/
const data = new Date()
const time = data.getFullYear() + '-' + (data.getMonth() + 1) + '-' + data.getDate() + '-' + data.getHours() + '-' + data.getMinutes()
const remoteNginxPath = '/usr/local/nginx/' // 远程服务器的路径,结尾需要 / ( /usr/local/nginx/ 是nginx的源码 )
const remoteProjectPath = '/opt/web/' // 远程服务器项目的路径
// const remotePath = '/usr/local/nginx/' // 远程服务器的路径,结尾需要 / ( /usr/local/nginx/ 是nginx的源码 )
const projectName = 'gzmxweb' // 远程项目的名称,相当于dist
const historyProjectName = '2020-7-16-16-51' // 这个在回滚上一个版本的时候需要手动修改,滚动的版本号,例如:2019-4-17-20

const gulpConfig = {
  devServerSShConfig: {
    uploadFile: './dist/**',
    sshConfig: {
      //   remotePath:'/root/nginx_szcg/website/zhifa/dist',
      remotePath: remoteProjectPath + projectName, // 远程网站地址,会自动新建projectName文件夹
      ssh: { // 外网测试
        host: '172.16.121.74',
        port: 22,
        username: 'root',
        password: 'password'
      },
      commands: [
        // 删除现有文件
        // `rm -rf /root/nginx_szcg/website/zhifa/ dist` ( 1.删除项目目录 )
        'rm -rf ' + remoteProjectPath + projectName + '/*'
      ],
      backups: [
        // cd /root/nginx_szcg/website/zhifa/dist/  ( 2.进入项目目录 )
        'cd ' + remoteProjectPath + projectName + '/',
        // tar -zcvf /root/nginx_szcg/website/zhifa/dist-copy/2019-4-17-3-59.tar.gz  ( 3.压缩备份,不会自动创建备份目录 )
        'tar -zcvf ' + remoteProjectPath + projectName + '-copy/' + time + '.tar.gz *'
      ],
      rollback: [
        // tar -zxvf /root/nginx_szcg/website/zhifa/dist-copy/2019-4-17-3-59.tar.gz -C /root/nginx_szcg/website/zhifa/dist/(4.解压恢复)
        'tar -zxvf ' + remoteProjectPath + projectName + '-copy/' + historyProjectName + '.tar.gz -C ' + remoteProjectPath + projectName + '/'
      ],
      // 只有修改nginx服务器的配置文件才需要重启nginx
      reload: [
        // /usr/local/webserver/nginx/sbin/nginx -s stop ( nginx -s stop  OR  nginx -s reload OR nginx -s start)
        remoteNginxPath + 'sbin/nginx -s stop',
        // /usr/local/webserver/nginx/sbin/nginx -c /usr/local/webserver/nginx/conf/nginx.conf
        remoteNginxPath + 'sbin/nginx -c /usr/local/nginx/conf/nginx.conf'
      ]
    }

  },

  proServerSShConfig: {

  }
}

module.exports = gulpConfig

git-flow merge --no-ff
另外一点是利用git的husky工具可以规范git的提交规范,配合commitlint
可以校验commit -msg的信息,同时配合commitizen进行命令行引导式提交。
npm i husky @commitlint/config-conventional @commitlint/cli commitizen -D
npx husky install
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
npm i commitizen commitlint/config-conventional -D

// pakage.json
{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint src",
    "lint:create": "eslint  --init",
    "commit": "cz"
  },
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint && npm run test",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog"
    }
  }
}

commitlint配置文件如下:

// commitlint.config.js
module.exports = {
  // extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [1, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 72],
    'scope-case': [2, 'always', 'lower-case'],
    'subject-case': [1, 'never', ['sentence-case', 'start-case', 'pascal-case',
      'upper-case']],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'type-case': [2, 'always', 'lower-case'],
    'type-empty': [2, 'never'],
    'type-enum': [
      2,
      'always',
      ['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor',
        'revert', 'style', 'test', 'improvement']
    ]
  }
}

// build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
// ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交
// docs:文档更新
// feat:新增功能
// merge:分支合并 Merge branch ? of ?
// fix:bug 修复
// perf:性能, 体验优化
// refactor:重构代码(既没有新增功能,也没有修复 bug)
// style:不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
// test:新增测试用例或是更新现有测试
// revert:回滚某个更早之前的提交
// chore:不属于以上类型的其他类型
// git commit -m'feat: add commit valid'

  • 工具规范
    比如IDEA,我会设置一个editorconfig文件规范编辑器,这个需要在VScode装editor插件
// .editorconfig
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

再者其它工具如babel或者eslint的具体配置,会在项目根目录创建文件来配置

// babel.config.js
module.exports = {
  // babel不用编译node_module下的文件
  exclude: /node_modules/,
  presets: [
    '@vue/cli-plugin-babel/preset', 
    // polyfill 配置ie兼容
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'entry',
        corejs: 3
      }
    ]
  ],
  plugins: [
    // polyfill 配置ie兼容
    [
      '@babel/plugin-transform-runtime',
      {
        absoluteRuntime: false,
        corejs: false,
        helpers: true,
        regenerator: true,
        useESModules: false
      }
    ],
    // element-ui按需加载
    [
      'component',
      {
        libraryName: 'element-ui',
        styleLibraryName: 'theme-chalk'
      }
    ]
  ]
}

相关文章

  • 我理解的项目规范

    结构规范对项目进行分层,比如网络层,组件层,路由层,工具层,存储层,他们表现为一个个具体的文件夹,其中文件夹的名字...

  • vite+vue3+ts+pinia+element-plus搭

    配置项目规范editorconfig+eslint+prettier 详细介绍可以看一下这篇文章代码规范之-理解E...

  • 一句话项目管理(持续更新)

    项目管理就是理解公司战略目标,实现公司战略目标; 项目管理就是制定计划、流程、规范,并监督执行; 就是让项目团队按...

  • Vue 项目规范

    Vue 项目规范 vue项目规范以https://cn.vuejs.org/v2/style-guide/的A规范...

  • 问题整理

    前端工程化的理解 【流程+规范+自动化等】 webpack(项目打包) 用过哪些loader和plugin;loa...

  • 今日碎碎念

    今天跟朋友聊起项目管理,我说其实我现在做的也不是规范的项目管理,她说现实生活中就没有规范的项目管理,任何一个项目都...

  • 1.1WEB项目虚拟环境搭建

    1、建立项目 首先,以规范的形式描述 其次,建立虚拟环境 最后,创建项目 1 .1 制定规范 完整的规范,说明项目...

  • 途度

    1.首页 2.出国圈 3.申请 4.我的 项目切图命名规范 项目UI设计规范

  • [规范]规范汇总

    开源项目规范 https://opensource.guide/ 项目日志更新规范 http://keepacha...

  • 项目管理内容

    1、项目进度把控(开发时间、测试接入) 2、项目质量把控(代码规范、接口规范、文档规范) 3、项目人力把控 4、项...

网友评论

      本文标题:我理解的项目规范

      本文链接:https://www.haomeiwen.com/subject/nnlaxrtx.html