沐乃

沐乃 查看完整档案

北京编辑哈尔滨工业大学  |  软件工程 编辑京东  |  软件开发工程师 编辑 x.y 编辑
编辑

The outer world you see is a reflection of your inner self.

个人动态

沐乃 发布了文章 · 2020-05-02

Vue项目中接入 StoryBook 展示组件示例

What

Storybook官网
隔离UI组件,支持独立调试&测试,统一展示项目组件库。

Why

项目中抽离的组件较多,但并没有集中展示出来具体有些什么,而是分布在各个使用的业务场景中。对于新接触项目的开发人员来说,由于不清楚业务场景,考虑复用组件时不是很直观。

How

最新搭建方案(2020.12.30)

官方README

一个组件接入storybook的示例

1.三行命令迅速启动示例页

npx -p @storybook/cli sb init

# 启动
npm run storybook

# 启动时报错提示找不到这个包 安装下下再启动
npm install @vue/babel-preset-app --save-dev

成功启动:
image.png

跟旧版本相比,自动生成的文件中demo更完善,上手更方便啦
image.png

2.package.json中添加脚本

{ 
    "scripts": { 
        "storybook": "start-storybook -p 6006",
        "build-storybook": "build-storybook -o ./docs"
    } 
}

./docs是输出的静态文件夹,不指定的话默认是storybook-static

旧版本搭建方案(@storybook/vue v5.3.x)

参考官方指南,选择Manual setup

1.添加依赖

$ cnpm install @storybook/vue -S

$ cnpm install vue-loader vue-template-compiler @babel/core babel-loader babel-preset-vue -S

2.package.json中添加脚本

{ 
    "scripts": { 
        "storybook": "start-storybook" 
    } 
}

3.添加less-loader,注入全局less变量

// .storybook/main.js
const path = require('path')

module.exports = {
  webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    // Make whatever fine-grained changes you need
    config.module.rules.push({
      test: /\.less/,
      use: [
        'style-loader',
        'css-loader',
        'less-loader',
        {
          loader: 'style-resources-loader',
          options: {
            patterns: [
              path.resolve(__dirname, '../src/assets/styles/variable.less')
            ]
          }
        }
      ],
      include: path.resolve(__dirname, '../')
    })

    return config
  }
}

4.添加路径别名

// .storybook/main.js
config.resolve.alias = {
    ...config.resolve.alias,
    '@': path.resolve(__dirname, '../src'),
    '@com': path.resolve(__dirname, '../src/components')
}

5.启动

$ npm run storybook

最小版本组件
storybook运行示例.png

项目结构

项目结构.png

// .storybook/config.js
import { configure } from '@storybook/vue'

configure(require.context('./stories', true, /\.stories\.js$/), module)
// .storybook/stories/global.stories.js
import Vue from 'vue'

import Toggle from './../../src/components/toggle/index.vue'

Vue.component('Toggle', Toggle)

export const toggle = () => `
 <toggle :data="[
        { label: 'toggle1', value: '1' },
       { label: 'toggle2', value: '2' }
      ]"
    />
`

export default { title: 'Global' }

最小版本over~
接下来把组件们都写进stories就大功告成啦~

一个小插曲

几天之后,npm run storybook启不起来一直报错缺依赖,直接rimraf node_modules/重新npm i 完事儿~

Addons

Knobs

动态交互展示组件属性

安装

cnpm install @storybook/addon-knobs -S

.storybook/main.js中引入插件

module.exports = {
  addons: ['@storybook/addon-knobs/register']
}

使用

// .storybook/stories/panel.stories.js
import { storiesOf } from '@storybook/vue'
import { withKnobs, boolean, text } from '@storybook/addon-knobs'

import JhlDialogDetail from '@com/jhl-dialog-detail/index.vue'

export default {
  title: 'Panel',
  decorators: [withKnobs]
}

export const egJhlDialogDetail = () => ({
  components: { JhlDialogDetail },
  props: {
    visible: { default: boolean('visible', true) },
    title: { default: text('Text', '标题') },
    dlgWidth: { default: text('dlgWidth', '66%') }
  },
  data () {
    return {
      data: [
        {
          label: '基本信息',
          children: [
            { label: '中文名', prop: 'labelNameCn', span: 8 },
            { label: '英文名', prop: 'labelNameEn', span: 8 },
            { label: '实体', prop: 'entityName', span: 8 },
            { label: '一级主题', prop: 'firstThemeName', span: 8 },
            { label: '二级主题', prop: 'secondThemeName', span: 8 },
            { label: '三级主题', prop: 'thirdThemeName', span: 8 }
          ]
        },
        {
          label: '加工信息',
          children: [
            { label: '更新时间', prop: 'labelModifiedTime', span: 8 },
            { label: '业务负责人', prop: 'businessOwnerErp', span: 16, render: 'erp' },
            { label: '口径', prop: 'processDesc', span: 24, render: 'html' }
          ]
        },
        { label: '自定义区域', slot: 'quality' }
      ],
      detail: {
        'labelNameCn': 'zm测试',
        'labelNameEn': 'zmTest',
        'entityName': '用户',
        'firstThemeName': '人口属性',
        'secondThemeName': '自然属性',
        'thirdThemeName': '年龄',
        'labelModifiedTime': '2020-05-14 00:00:00',
        'businessOwnerErp': 'zhaozimu',
        'processDesc': '<p>213来源于X表,来源表关联字段X,标签筛选规则是XX,时间窗口为近X天,标签加工逻辑为XX,标签覆盖量为X,标签更新周期为XX</p>'
      }
    }
  },
  template:
    `<jhl-dialog-detail
            :visible.sync="visible"
            :title="title"
            :dlg-width="dlgWidth"
            :data="data"
            :detail="detail"
          >
            <div slot="quality">'slot' is here~</div>
          </jhl-dialog-detail>`
})

效果

knobs测试.jpg

Actions

快速上手

由于组件里都是$emit抛的事件,Actions暂时还不支持,待后续研究补充~

storybook-readme

快速上手

展示markdown文档

安装

cnpm i storybook-readme -D

注册组件

// .storybook/addons.js
import 'storybook-readme/register'

使用

配置

// .storybook/config.js
import { configure, addDecorator, addParameters } from '@storybook/vue'
// UI framework
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// knobs
import { withKnobs } from '@storybook/addon-knobs'
// readme
import { addReadme } from 'storybook-readme/vue'
import { themes, create } from '@storybook/theming'

const basicTheme = create({
  base: 'light',
  brandTitle: 'README addon',
  brandUrl: 'https://github.com/tuchk4/storybook-readme',
  brandImage: null
})

addParameters({
  options: {
    showPanel: true,
    panelPosition: 'right',
    theme: basicTheme
    // theme: themes.dark,
  },
  readme: { codeTheme: 'github' }
})

addDecorator(withKnobs)
addDecorator(addReadme)

Vue.use(ElementUI)

configure(require.context('./stories', true, /\.stories\.js$/), module)

使用

//.storybook/stories/toggle.stories.js
import { boolean, number } from '@storybook/addon-knobs'

import Toggle from '@com/toggle/index.vue'
import ToggleReadme from '@com/toggle/index.md'

export default {
  title: 'Component',
  parameters: {
    readme: { sidebar: ToggleReadme }
  }
}

export const egToggle = () => ({
  components: { Toggle },
  props: {
    isResetIdx: boolean('isResetIdx', false),
    currentIndex: number('currentIndex', 1)
  },
  data () {
    return {
      data: [
        { label: 'toggle1', value: '1' },
        { label: 'toggle2', value: '2' },
        { label: 'toggle3', value: '3', status: 'false' }
      ]
    }
  },
  template:
    `<toggle 
        :data="data"
        :is-reset-idx="isResetIdx"
        :current-index="currentIndex"
    />`
})

效果

文档效果.jpg

查看原文

赞 2 收藏 2 评论 2

沐乃 发布了文章 · 2020-02-29

CICD持续集成: .gitlab-ci.yml配置小记(gitlab-ci + gitlab-runner)

参考博文:基于gitlab-ci的CICD

概念

GitLab-CI
GitLab自带的持续集成系统,负责.gitlab-ci.yml脚本解析。

GitLab-Runner
脚本执行的承载者。GitLab-CI解析.gitlab-ci.yml文件后,根据里面配置的规则,分配到各个Runner来运行相应的脚本script。

.gitlab-ci.yml
位于git项目根目录,记录了一系列的阶段和执行规则。

Pipeline
一次 Pipeline 其实相当于一次构建任务,里面可以包含多个流程,如安装依赖、运行测试、编译、部署测试服务器、部署生产服务器等流程。

Stages
构建阶段(上述所说的流程)。一次 Pipeline 中可以定义多个 Stages,所有 Stages 会按顺序运行,前一个 Stage 完成后,下一个才会开始。只有当所有 Stages 完成后,该构建任务 (Pipeline) 才会成功。任何一个 Stage 失败,后面的 Stages 都不会执行,该构建任务失败。

Jobs
构建工作,某个 Stage 里面执行的具体内容。我们可以在 Stages 里面定义多个 Jobs,相同 Stage 中的 Jobs 会并行执行。相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功,如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 失败。

流程

1575862161805498.png

安装部署

添加gitlab官方库

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash

安装最新版本的gitlab-runner

yum -y install gitlab-runner

启动服务

gitlab-runner list 查看各个 Runner 的状态
gitlab-runner stop 停止服务
gitlab-runner start 启动服务
gitlab-runner restart 重启服务

注册

需要先获取令牌。详细步骤请移步文章顶部参考博文。

.gitlab-ci.yml配置

这个文件大家参考意义不大,调用的命令很多是自定义的。博主主要是想记录下。

variables:
  # 构建结果发送邮件(服务端python实现)
  RECIEVER: xxx@yyy.com;
  SYS_NAME: jinghailu
  BASE_DIR: /export/App/
  BACKUP_DIR: /export/App/bak/
  BUILD_DIR: ./_build/
  DEV_ALPHA_IP: **.**.**.**
  DEV_DATABANK_IP: **.**.**.**
  # 三个IP分别对应国内、泰国、印尼
  UAT_IP: **.**.**.** **.**.**.** **.**.**.**

.env_init: &env_init |
  # develop / master
  IP_LIST="${UAT_IP}"

  # 从分支dev-alpha提交代码并push
  if [[ "${CI_COMMIT_REF_NAME}" == "dev-alpha" ]]; then
  IP_LIST="${DEV_ALPHA_IP}"
  fi

  # 从分支dev-databank提交push
  if [[ "${CI_COMMIT_REF_NAME}" == "dev-databank" ]]; then
  IP_LIST="${DEV_DATABANK_IP}"
  fi

  #if [[ -n "${CI_COMMIT_TAG}" ]]; then
  #IP_LIST="${UAT_IP}"
  #fi

before_script:
  - *env_init
# - PATH=$PATH:/export/servers/node-v10.15.1/bin/

cache:
  key: "$CI_COMMIT_REF_NAME"
  paths:
    - node_modules/
    - deploy.tar.gz

# for uat env
package-develop:
  stage: build
  script:
    - npm install
    - npm run build:uat
    - mkdir $BUILD_DIR
    - cp -r ./dist/ $BUILD_DIR/resource
    - cp -r ./jdos/* $BUILD_DIR
    - chmod a+x $BUILD_DIR/bin/*
    - cd $BUILD_DIR && tar czvf ../deploy.tar.gz * && cd ..
    - download_url=`upload-deploy-package -f deploy.tar.gz -r ${SYS_NAME}/${CI_COMMIT_REF_NAME}.tar.gz`
    - my-sendmail -s "publish-${SYS_NAME}" -c "You could deploy in jdos now! </br></br> <b>Branch:</b>&nbsp;${CI_COMMIT_REF_NAME} </br> <b>Commit:</b>&nbsp;${CI_COMMIT_MESSAGE} </br> <b>Commit Author:</b>&nbsp;${GITLAB_USER_NAME} </br> <b>Download Url:</b>&nbsp;${download_url}" -r "$RECIEVER"
  only:
    - develop
    - dev-alpha
    - dev-databank

# for master env
package-master:
  stage: build
  script:
    - npm install
    - npm run build
    - mkdir $BUILD_DIR
    - cp -r ./dist/ $BUILD_DIR/resource
    - cp -r ./jdos/* $BUILD_DIR
    - chmod a+x $BUILD_DIR/bin/*
    - cd $BUILD_DIR && tar czvf ../deploy.tar.gz * && cd ..
    - download_url=`upload-deploy-package -f deploy.tar.gz -r ${SYS_NAME}/${CI_COMMIT_REF_NAME}.tar.gz`
    - my-sendmail -s "publish-${SYS_NAME}" -c "You could deploy in jdos now! </br></br> <b>Branch:</b>&nbsp;${CI_COMMIT_REF_NAME} </br> <b>Commit:</b>&nbsp;${CI_COMMIT_MESSAGE} </br> <b>Commit Author:</b>&nbsp;${GITLAB_USER_NAME} </br> <b>Download Url:</b>&nbsp;${download_url}" -r "$RECIEVER"
  only:
    - master

# Auto Deploy
deploy:
  stage: deploy
  script:
    - echo $IP_LIST
    - for ip in $IP_LIST; do
    - echo $ip
    # backup files
    - ssh admin@$ip "cd $BASE_DIR && mkdir -p $BACKUP_DIR && tar czvf /export/App/bak/resource-`date +%Y%m%d%H`.tar.gz resource/ bin/"
    # stop service
    - ssh admin@$ip "cd /export/App/bin && /export/App/bin/stop.sh"
    # send file
    - scp deploy.tar.gz admin@$ip:$BASE_DIR
    # deploy files
    - ssh admin@$ip "cd $BASE_DIR && \rm -rf ./resource && tar xzvf deploy.tar.gz && \rm deploy.tar.gz"
    # work only in pre-production env
    # - ssh admin@$ip "cd $BASE_DIR/nginx && \cp jinghailu.jd.com_yufa agent"
    # start service
    - ssh admin@$ip "cd /export/App/bin && /export/App/bin/start.sh"
    - done
    - my-sendmail -s "deploy-${SYS_NAME}" -c "Deployed successfully! </br></br> <b>Branch:</b>&nbsp;${CI_COMMIT_REF_NAME} </br> <b>Commit:</b>&nbsp;${CI_COMMIT_MESSAGE} </br> <b>Commit Author:</b>&nbsp;${GITLAB_USER_NAME}" -r "$RECIEVER"
  only:
    - master
    - develop
    - dev-alpha
    - dev-databank

笔记

处理流程:

  1. 制定分支push
  2. 构建阶段 - 安装依赖(服务端每次拉取最新代码时更新依赖库)
  3. 构建阶段 - 执行打包命令
  4. 构建阶段 - 压缩包传到oss平台
  5. 构建阶段 - 发送邮件,附oss访问地址
  6. 部署阶段 - 遍历IP,从oss拉包到部署服务器,启动脚本

Tip:

  • 相同 Stage 中的 Jobs 会并行执行
  • job越少耗时越短。尝试过拆分更细粒度的阶段,阶段越多耗时越长。故最终还是只用了2个阶段。

1582977998917.jpg

查看原文

赞 0 收藏 0 评论 0

沐乃 收藏了文章 · 2019-12-05

Javascript如何与Sass,Less,Css之间共享变量?

原博文地址,此博文首次发于csdn,原创

前段时间遇到网站换肤的需求,本想挺简单的,直接用 Sass 或者 Less 设置变量就好了,结果导致的问题就是 js 中设置的样式

得单独设置,好麻烦。

本文章代码gitHub地址 https://github.com/l-x-f/variables-css-less-sass-js

这个时候如果能让样式文件和 js 文件实现变量共享,那这个问题就解决了。查了好多资料,最终的解决方案如下:

1.原理介绍:通过 webpack 和 css module,我们可以在 js 中使用样式 Sass,Less,Css 文件中定义的变量。

2.环境: node: v8.10.0 vue-cli:3.5.3

3.package.json ( scss和less及其loader等先安装,vue-cli:3.5.3 下webpack已配置)

{
  "name": "Javascript 如何 Sass,Less,Css 之间共享变量",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^2.6.5",
    "vue": "^2.6.6",
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.5.0",
    "@vue/cli-plugin-eslint": "^3.5.0",
    "@vue/cli-service": "^3.5.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.8.0",
    "eslint-plugin-vue": "^5.0.0",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "node-sass": "^4.9.0",
    "sass-loader": "^7.1.0",
    "vue-template-compiler": "^2.5.21"
  }
}

4.文件目录
在这里插入图片描述

5.variables文件夹下代码(核心代码)
css.css文件

/* @value 定义变量,然后使用。

@value bgcColor: red;   会导出@ 后面的变量,
在:export {}  可以取到该变量,也可以导出
他们在功能上等同于 ES6 的关键字export,即导出一个 js 对象。*/

@value bgcColor: red;
@value fontSize: 10px;

/*   这里可以直接使用
.demo { 
  background-color: bgcColor;
  font-size: fontSize;
} */


/*  CSS Module  导出 
 :export {
  cssExportBgcColor: bgcColor;
}
 */

less.less文件

@mainColor: #398bd0;
@fontSize: 100px;

// CSS Module 有一个:export关键字,它在功能上等同于 ES6 的关键字export,即导出一个 js 对象。
:export {
  name: "less";
  mainColor: @mainColor;
  fontSize: @fontSize;
}

scss.scss文件

$primaryColor: #f4d020;
$fontSize: 11px;

// CSS Module 有一个:export关键字,它在功能上等同于 ES6 的关键字export,即导出一个 js 对象。
:export {
  name: "scss"; 
  primaryColor: $primaryColor;
  fontSize: $fontSize;
}

index.js文件

import variablesScss from "./scss.scss";
import variablesLess from "./less.less";
import variablesCss from "./css.css";

// 导出变量
export default {
  variablesScss,
  variablesLess,
  variablesCss
};

6.styles文件夹下代码(样式文件中使用变量)

css-use.css文件

@value variables: "../variables/css.css"; /* 导入变量*/
@value bgcColor, fontSize from variables;/* 取出变量*/

.box {
  name: "css"; /* 此属性错误,仅用于区分,可忽略*/
  background-color: bgcColor;
  font-size: fontSize;
}

less-use.less文件

@import "../variables/less.less";

.box {
  name: "less";/* 此属性错误,仅用于区分,可忽略*/
  background-color: @mainColor;
  font-size:  @fontSize;
 
}

scss-use.scss文件

@import "../variables/scss.scss";

.box {
  name: "scss"; /* 此属性错误,仅用于区分,可忽略*/
  background-color: $primaryColor;
  font-size: $fontSize;
 
}

7.main.js代码

import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
import variables from "./variables";

console.log(variables, "main-variables");

new Vue({
  render: h => h(App)
}).$mount("#app");

8.App.vue文件代码

<template>
  <div id="app" class="box"></div>
</template>

<script>
// variables in  js
import variables from "./variables";
export default {
  name: "home",
  components: {},
  data() {
    return {
      variables: { ...variables }
    };
  },
  created() {
    console.log(this.variables, "App-this.variables");
  }
};
</script>

<!--css -->
<style lang="css">
#app {
  height: 500px;
}
@import "./styles/css-use.css";
</style>


<!--scss -->
<style lang="scss">
@import "./styles/scss-use.scss";
</style>

<!-- less-->
<style lang="less">
@import "./styles/less-use.less";
</style>

9.最后的效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如图我们在js文件(或vue 文件)中取到样式变量,最后引用第三个资料中的一句话结束本文章

环境之间共享变量是编程的圣杯

参考资料
http://www.ruanyifeng.com/blo...
https://github.com/css-module...
https://www.bluematador.com/b...
https://github.com/css-module...
https://github.com/css-module...

查看原文

沐乃 赞了文章 · 2019-12-05

Javascript如何与Sass,Less,Css之间共享变量?

原博文地址,此博文首次发于csdn,原创

前段时间遇到网站换肤的需求,本想挺简单的,直接用 Sass 或者 Less 设置变量就好了,结果导致的问题就是 js 中设置的样式

得单独设置,好麻烦。

本文章代码gitHub地址 https://github.com/l-x-f/variables-css-less-sass-js

这个时候如果能让样式文件和 js 文件实现变量共享,那这个问题就解决了。查了好多资料,最终的解决方案如下:

1.原理介绍:通过 webpack 和 css module,我们可以在 js 中使用样式 Sass,Less,Css 文件中定义的变量。

2.环境: node: v8.10.0 vue-cli:3.5.3

3.package.json ( scss和less及其loader等先安装,vue-cli:3.5.3 下webpack已配置)

{
  "name": "Javascript 如何 Sass,Less,Css 之间共享变量",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^2.6.5",
    "vue": "^2.6.6",
    "vue-router": "^3.0.1",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.5.0",
    "@vue/cli-plugin-eslint": "^3.5.0",
    "@vue/cli-service": "^3.5.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.8.0",
    "eslint-plugin-vue": "^5.0.0",
    "less": "^3.9.0",
    "less-loader": "^4.1.0",
    "node-sass": "^4.9.0",
    "sass-loader": "^7.1.0",
    "vue-template-compiler": "^2.5.21"
  }
}

4.文件目录
在这里插入图片描述

5.variables文件夹下代码(核心代码)
css.css文件

/* @value 定义变量,然后使用。

@value bgcColor: red;   会导出@ 后面的变量,
在:export {}  可以取到该变量,也可以导出
他们在功能上等同于 ES6 的关键字export,即导出一个 js 对象。*/

@value bgcColor: red;
@value fontSize: 10px;

/*   这里可以直接使用
.demo { 
  background-color: bgcColor;
  font-size: fontSize;
} */


/*  CSS Module  导出 
 :export {
  cssExportBgcColor: bgcColor;
}
 */

less.less文件

@mainColor: #398bd0;
@fontSize: 100px;

// CSS Module 有一个:export关键字,它在功能上等同于 ES6 的关键字export,即导出一个 js 对象。
:export {
  name: "less";
  mainColor: @mainColor;
  fontSize: @fontSize;
}

scss.scss文件

$primaryColor: #f4d020;
$fontSize: 11px;

// CSS Module 有一个:export关键字,它在功能上等同于 ES6 的关键字export,即导出一个 js 对象。
:export {
  name: "scss"; 
  primaryColor: $primaryColor;
  fontSize: $fontSize;
}

index.js文件

import variablesScss from "./scss.scss";
import variablesLess from "./less.less";
import variablesCss from "./css.css";

// 导出变量
export default {
  variablesScss,
  variablesLess,
  variablesCss
};

6.styles文件夹下代码(样式文件中使用变量)

css-use.css文件

@value variables: "../variables/css.css"; /* 导入变量*/
@value bgcColor, fontSize from variables;/* 取出变量*/

.box {
  name: "css"; /* 此属性错误,仅用于区分,可忽略*/
  background-color: bgcColor;
  font-size: fontSize;
}

less-use.less文件

@import "../variables/less.less";

.box {
  name: "less";/* 此属性错误,仅用于区分,可忽略*/
  background-color: @mainColor;
  font-size:  @fontSize;
 
}

scss-use.scss文件

@import "../variables/scss.scss";

.box {
  name: "scss"; /* 此属性错误,仅用于区分,可忽略*/
  background-color: $primaryColor;
  font-size: $fontSize;
 
}

7.main.js代码

import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
import variables from "./variables";

console.log(variables, "main-variables");

new Vue({
  render: h => h(App)
}).$mount("#app");

8.App.vue文件代码

<template>
  <div id="app" class="box"></div>
</template>

<script>
// variables in  js
import variables from "./variables";
export default {
  name: "home",
  components: {},
  data() {
    return {
      variables: { ...variables }
    };
  },
  created() {
    console.log(this.variables, "App-this.variables");
  }
};
</script>

<!--css -->
<style lang="css">
#app {
  height: 500px;
}
@import "./styles/css-use.css";
</style>


<!--scss -->
<style lang="scss">
@import "./styles/scss-use.scss";
</style>

<!-- less-->
<style lang="less">
@import "./styles/less-use.less";
</style>

9.最后的效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如图我们在js文件(或vue 文件)中取到样式变量,最后引用第三个资料中的一句话结束本文章

环境之间共享变量是编程的圣杯

参考资料
http://www.ruanyifeng.com/blo...
https://github.com/css-module...
https://www.bluematador.com/b...
https://github.com/css-module...
https://github.com/css-module...

查看原文

赞 7 收藏 6 评论 1

沐乃 发布了文章 · 2019-10-29

Nginx配置:读取自定义header + 撰写AND条件 + 修改响应体 + 域名重定向

需求

不修改代码的情况下,在nginx转发请求时修改response body获取期望值。

请求有2个特征唯一定位:

  1. url:/aaa/bbb/ccc/ddd
  2. 自定义header(区分版本):System-Version: pro 不同版本会请求相同服务器下相同url

Tip:这么干的原因主要是由于上线管控,问题修复后不能随时上线。但是线上部分用户已经不能正常使用功能了,所以算是一个暂时解决方案,不提倡不鼓励这么干哇~

原返回值:

{
  "code": 200,
  "data": {
    "deployAdmin": false,
    "systemId": "aaa"
  }
}

期望返回值:

// deployAdmin控制页面显示,这里期望为true
{
  "code": 200,
  "data": {
    "deployAdmin": true,
    "systemId": "aaa"
  }
}

实现

基于基本nginx配置,不需安装额外模块(安装额外模块得找运维,也赶不及-_-)。

读取自定义header

首先,开启underscores_in_headers

解释:HTTP头部是否允许下划线。默认为off,表示HTTP头部的名称中不允许带“_”
语法:underscores_in_headers on | off;
默认:underscores_in_headers off;
配置块:http、server

然后,通过$http_加自定义header读取目标请求头(纯小写,中划线替换为下划线)
比如:获取 System-Version 应写为 $http_system_version

# nginx.conf
server {
    listen          80;
    ...
    underscores_in_headers on;  
    ...
    location ~ ^/aaa/ {
        ... 
        if ($http_system_version = "pro") {
            ...
        }
    }
}

撰写AND条件

nginx配置中使用 if(a && b) 的条件判断是无效的。但需求中要求的条件是 url = '/aaa/bbb/ccc/ddd' 并且 $http_system_version = 'pro' 。解决思路是声明一个变量,每判断一个条件为true,就将变量修改一下,最后直接判断该变量是否达到了所有条件为true的修改状态即可。

# nginx.conf

location ~ ^/aaa/ {
    ...
    
    set $a 0;
    if ($http_system_version = "pro"){
        set $a 1;
    }
    if ($request_uri ~ ^/aaa/bbb/ccc/ddd) {
        set $a 1$a;
    }
    if ($a = '11') {
        ... 
    }
}

修改响应体

直接上这段逻辑完整配置

location ~ ^/aaa/ {
    # 以/aaa开头的请求代理到 http://xxx.yyy.com
    proxy_pass http://xxx.yyy.com;
    proxy_redirect          off; 
    proxy_set_header        X-Real-IP       $remote_addr; 
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # AND条件判断变量
    set $a 0;
    # 请求中自定义header头System-Version为"pro"
    if ($http_system_version = "pro") {
        set $a 1;
    }
    # 请求url为'/aaa/bbb/ccc/ddd'
    if ($request_uri ~ ^/aaa/bbb/ccc/ddd) {
        set $a 1$a;
    }
    # 两个条件都符合时return 期望返回值
    if ($a = '11') {
        return 200 '{"code":200,"data":{"deployAdmin":true,"systemId":"aaa"}}';
    }
}

Tip: if后一定要跟空格,不然nginx会报错~

域名重定向

小记一下,跟本次需求无关联

服务器对应两个域名123.yy.com456.yy.com,用户访问123.yy.com时,将请求重定向到456.yy.com(此时浏览器地址栏url域名会被替换)。这种情况适用与新老域名过渡期~

if ($host = '123.yy.com') {
    rewrite ^/(.*)$ http://456.yy.com/$1 permanent;
}

小记

有一个过滤器模块 ngx_http_sub_module,它可以通过将一个指定的字符串替换为另一个字符串来响应,但需要安装,默认不带。

另:更灵活的配置可通过搭配lua

查看原文

赞 1 收藏 1 评论 0

沐乃 发布了文章 · 2019-10-19

Element + Vue I18n动态import加载国际化语言包翻译文件

需求

项目为多页应用,包含产品a、b、c、d、e,每个产品都有自己的翻译文件。
一次加载所有翻译文件是极度不合理的。于是考虑动态加载。

实现

参考官方文档:延迟加载翻译

项目结构

│
├── dist // 静态资源输出目录
│
├── src
│   ├── assets
│   ├── components
│   ├── lang // 语言翻译文件
│        ├── a
│            ├── en_US.js
│            └── zh_CN.js
│        ├── b
│            ├── en_US.js
│            └── zh_CN.js
│        ├── c
│            ├── en_US.js
│            └── zh_CN.js
│        ├── d
│            ├── en_US.js
│            └── zh_CN.js
│        ├── e
│            ├── en_US.js
│            └── zh_CN.js
│   ├── pages
│        ├── a
│        ├── b
│        ├── c
│        ├── d
│        ├── e
│   ├── utils
│        ├── i18n.js 
│        ├── ...

i18n.js

import Vue from 'vue'
import VueI18n from 'vue-i18n'

import ElementLocale from 'element-ui/lib/locale'
import enLocale from 'element-ui/lib/locale/lang/en'
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'

// LANG:全局变量,从cookie中获取的当前语言版本,'zh_CN'、'en_US'
// IS_INTL:全局变量,是否为国际版
import { LANG, IS_INTL } from '@/constant'

Vue.use(VueI18n)

const i18n = new VueI18n({
  silentTranslationWarn: true
})
// 这里需要覆盖Element本地化函数,不然会冲突
ElementLocale.i18n((key, value) => i18n.t(key, value))
export default i18n

/**
 * 更改vueI18n实例
 * @param lang 'zh_CN'、'en_US'
 */
function setI18nLang (lang) {
  i18n.locale = lang
  // axios.defaults.headers.common['Accept-Language'] = lang
  document.querySelector('html').setAttribute('lang', lang)
  return lang
}

/**
 * 动态加载语言
 * @param path 当前路径(对应lang下文件夹名)
 */
const _LANGS = ['zh_CN', 'en_US']
const ELEMENT_LANG = {
  zh_CN: zhLocale,
  en_US: enLocale
}
let loadedLanguages = []

export function loadLanguageAsync (path) {
 // lang目前是从cookie中获取,所以没有作为参数传递,此处lang也可作为变量传递

 // 如果cookie中没有获取到语言项,国际版默认初始化为英文
  const lang = LANG || (IS_INTL ? 'en_US' : 'zh_CN')

  if (!path || !_LANGS.includes(lang)) return

  if (i18n.locale !== lang) {
    if (!loadedLanguages.includes(lang)) {
      // 文件大时可拆分打包,目前项目中翻译文件均为4、5K左右,就没拆
      // return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${path}/${lang}`).then(msgs => {
      return import(`@/lang/${path}/${lang}`).then(msgs => {
        const _temp = Object.assign(msgs.default, ELEMENT_LANG[lang])
        i18n.setLocaleMessage(lang, _temp)
        loadedLanguages.push(lang)
        return setI18nLang(lang)
      })
    }
    return Promise.resolve(setI18nLang(lang))
  }
  return Promise.resolve(lang)
}

main.js

import Vue from 'vue'
import App from './label.vue'
import store from './store/index'
import router from './router/index'

import i18n, { loadLanguageAsync } from '@/utils/i18n'
import '@label/plugins/element-ui'
import '@/assets/styles/common.less'
import AppComponents from '@label/components/index' // 注册全局组件

Vue.config.productionTip = false
Vue.use(AppComponents)

// a页
loadLanguageAsync('a').then(_ => {
  new Vue({
    i18n,
    store,
    router,
    render: h => h(App)
  }).$mount('#app')
})

切换语言组件

主要操作:切换时setCookie后重刷页面
原因:本来直接调用loadLanguageAsync是可以正常重刷语言包的,但是这里由于页面某些显示内容是来源于后端接口的,还是得重新请求,所以重刷了整页

setCookie('jd.erp.lang', lang || 'en_US', 1, { path: '/', domain: CUR_HOST_SUFFIX })
window.history.go(0)

组件内容如下:

// langs.vue
<template>
  <el-dropdown
    class="icon-info icon-langs"
    @command="changeLang"
  >
    <img :data-original="imgSrc">
    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item
        v-for="item in langs"
        :key="item.value"
        :command="item.value"
        :disabled="item.value === locale"
      >
        {{ item.label }}
      </el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
import { LANG, CUR_HOST_SUFFIX } from '@/constant'
import { setCookie } from '@/utils/utils'

export default {
  name: 'langs',
  data () {
    return {
      imgSrc: require('@label/assets/icons/header-langs.svg'),
      locale: LANG,
      langs: [
        { label: '中文', value: 'zh_CN' },
        { label: 'English', value: 'en_US' }
      ]
    }
  },
  methods: {
    changeLang (lang) {
      this.locale = lang
      setCookie('jd.erp.lang', lang || 'en_US', 1, { path: '/', domain: CUR_HOST_SUFFIX })
      window.history.go(0)
    }
  }
}
</script>
查看原文

赞 2 收藏 1 评论 0

沐乃 发布了文章 · 2019-09-17

hexo搭建github个人博客

需求

搭建github个人博客

环境

安装node环境

参考版本

环境版本
nodev10.15.1
npm6.9.0

移步官方详细说明:hexo官网

步骤

全局安装hexo

$ npm install -g hexo-cli

建站

# hexo-test为文件夹名
$ hexo init hexo-test
$ cd hexo-test
# 安装依赖
$ npm install
# 启动本地服务
$ hexo server

更换主题

挑选主题
这里选择主题'autumn'

# 下载主题资源到 themes 文件夹下
$ git clone https://github.com/FrontendSophie/hexo-theme-autumn.git

主题资源放入themes文件夹:
themes下加入主题

修改 _config.yml 配置:

theme: hexo-theme-autumn

启动本地服务

$ hexo server

主题替换成功:
图片描述

修改其他配置

参见配置 | Hexo

部署到github

首先,在github新建一个名为<github用户名>.github.io的仓库
图片描述

然后,安装hexo-deployer-git

$ npm install hexo-deployer-git --save

在 _congif.yml 中配置 deploy 字段

deploy:
  type: git
  repo: <你的仓库地址> # https://github.com/zimuZhao/zimuZhao.github.io
  branch: master

执行自动部署命令

$ hexo clean
$ hexo deploy

Deploy成功后访问:https://zimuzhao.github.io/

文章撰写示例

在目录source->_posts下新建md文档

文档部分示例(添加标题、作者、tag等)

---
title: hexo搭建github个人博客
author: ZIMU
tags:
  - hexo
categories:
  - demo
---

# 需求
搭建github个人博客

# 环境
安装[node环境][1]

...
查看原文

赞 0 收藏 0 评论 0

沐乃 关注了专栏 · 2019-06-09

前端小而全的知识归纳

vue,react,小程序,php,node乱炖

关注 1252

沐乃 发布了文章 · 2019-06-07

vue-cli3多页应用构建优化

接续前篇多页应用改造,优化改造后的项目打包效果。

效果对比

阶段磁盘占用空间打包时间备注
原始9.9M-5.20版
架构改造(SPA->MPA)7.1M-5.27版
样式整合7.0M-5.30版
公共组件&工具类整合6.9M-6.4版
图片压缩6.0M20.767s-
UglifyJs5.9M23.391s-
合并小文件5.3M55.469s-
代码分割splitChunks5.1M73.730s-
gzip6.3M89.204s-

优化前

优化前打包

优化后

优化后打包

优化过程

压缩图片

chainWebpack: config => {
  config.module
    .rule("image-webpack-loader")
    .test(/\.(gif|png|jpe?g|svg)$/i)
    .use("file-loader")
    .loader("image-webpack-loader")
    .tap(() => ({
      disable: process.env.NODE_ENV !== "production"
    }))
    .end()
};

添加UglifyJs,移除console

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      const plugins = []
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            compress: {
              drop_console: true,
              drop_debugger: true
            }
          },
          cache: true, // 启用文件缓存
          parallel: true // 使用多进程并行运行来提高构建速度
          // sourceMap: false // 映射错误信息到模块
        })
      )
      config.plugins = [
        ...config.plugins,
        ...plugins
      ]
    }
  }

动态导入合并小文件webpackChunkName

原来打包出来的文件,快上百个了,还有好些0.29KB的,需要合并下。此处使用webpackChunkName

首先按照业务含义合并,以service为例

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const routes = [
  {
    path: '/service',
    component: () => import(/* webpackChunkName: "service-index" */ '@service/components/page-layout/TwoColLayout.vue'),
    redirect: { name: 'Detail' },
    children: [
      {
        path: 'detail',
        name: 'Detail',
        component: () => import(/* webpackChunkName: "service-index" */ '@service/views/detail/index.vue')
      },
      /**
       * 接口申请(指标)
       */
      {
        path: 'api-apply',
        name: 'ApiApply',
        component: () => import(/* webpackChunkName: "service-api-apply" */ '@service/views/detail/views/apply-api/index.vue')
      },
      {
        path: 'api-apply-form',
        name: 'ApiApplyForm',
        component: () => import(/* webpackChunkName: "service-api-apply" */ '@service/views/detail/views/apply-api/api-apply-form.vue')
      },
      /**
       * 接口管理
       */
      {
        path: 'manage',
        name: 'Manage',
        component: () => import(/* webpackChunkName: "service-manage" */ '@service/views/manage/index.vue')
      },
      { // 接口管理 - 新建/修改
        path: 'manage/upsert',
        name: 'ManageUpsert',
        component: () => import(/* webpackChunkName: "service-manage" */ '@service/views/manage/module/upsert/index.vue')
      }]
  },
  {
    path: '*',
    redirect: '/service'
  }
]

const router = new Router({ mode: 'history', routes })

export default router

service合并后
看起来都比较小,太小的独立文件单占连接数不是很合适。那就service-manage保留(业务上此处只有管理员能访问的管理页会调用,一般人看不到,不用和其他逻辑一起加载,还是保持独立),
其他的再统一合并成service-main。这里合并的宗旨是文件不超过250K。

...
此处省略处理各个产品打包合并过程。

代码分割splitChunks

chunk-vendors-xxx.js空间分析
从打包目录看,chunk-vendors-xxx.js已达1.7M,需要拆分。

参考文章: 一步一步的了解webpack4的splitChunk插件

这里根据实际情况优化,具体大家可通过vue-cli-service build --report生成 report.html分析包内容
图片描述

首先拆出公用的element-ui

  configureWebpack: config => {
    if (IS_PROD) {
      config.optimization = {
        splitChunks: {
          cacheGroups: {
            element: {
              name: 'vendors-element-ui',
              test: /[\\/]node_modules[\\/]element-ui[\\/]/,
              chunks: 'initial',
              reuseExistingChunk: true,
              enforce: true,
              priority: 3
            },
          }
        }
      }
    }
  }

然后把所有node_modlues下的引用合并到vendors

...
cacheGroups: {
        vendors: {
          name: 'vendors',
          test: /[\\/]node_modules[\\/]/,
          chunks: 'all',
          priority: 2
        }
}
...

再将所有模块中被不同的 chunk 引入超过 1 次的抽取为 common

...
cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          maxInitialRequests: 5,
          priority: 1
        }
}
...

启用gzip

const CompressionWebpackPlugin = require('compression-webpack-plugin')
  configureWebpack: config => {
    if (IS_PROD) {
      const plugins = []
      plugins.push(
        new CompressionWebpackPlugin({
          filename: '[path].gz[query]',
          algorithm: 'gzip',
          test: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i,
          threshold: 10240,
          minRatio: 0.8
        })
      )
      config.plugins = [
        ...config.plugins,
        ...plugins
      ]
    }
  }

css根据入口文件打包

图片描述
到这里,打包出来的css文件都很小,单独放到一个资源文件去请求挺浪费连接数的,所以尝试合并css。

参考:Extracting CSS based on entry

目前暂未成功,可能与上述webpackChunkName方式冲突,合并后的css正常,但根据webpackChunkName之后的css依旧会打包出来,暂时没找到更好的解决办法。待更新~

警告处理

[mini-css-extract-plugin] Conflicting

图片描述

参考issue reported on Github

两种解决方法:

  1. 调整import顺序
  2. 配置忽略警告

选用方法1解决。

查看原文

赞 4 收藏 3 评论 0

沐乃 发布了文章 · 2019-06-04

vue-cli3添加模式配置多环境变量

vue-cli3配置多环境变量

先挂官网描述:环境变量和模式

需求

根据运行环境判断执行代码:

  • 预发环境操作完成跳转地址与线上环境跳转地址不同
  • 线上环境添加埋点脚本

实现

Step1:在项目根目录中新建.env.uat

.env.uat配置如下:

VUE_APP_BUILD_TYPE=uat

vue-cli中规定,只有以 VUE_APP_ 开头的变量会被 webpack.DefinePlugin 静态嵌入到客户端侧的包中。
代码中可以通过process.env.VUE_APP_SECRET这样访问。

  • NODE_ENVBASE_URL 是两个特殊变量,在代码中始终可用

Step2:修改 package.json

传递 --mode 选项参数为命令行覆写默认的模式。

"scripts": {
    "build": "vue-cli-service build",
    "build:uat": "vue-cli-service build --mode uat"
  }

Step3:使用

// 是否预发部署
const IS_UAT = process.env.VUE_APP_BUILD_TYPE === 'uat'
const _url =  IS_UAT ? 'http://a.123.com' : 'http://b.123.com'

// 是否添加埋点
const IS_ADD_JAQ = process.env.NODE_ENV === 'production' && !IS_UAT
if (IS_ADD_JAQ && loginUser) addJaq(loginUser)
 ...

优化

到这,功能已经实现了。

美滋滋地打个包,妈呀,体积也太大了:

模式修改后

此处解决办法为强写NODE_ENV,最终.env.uat配置如下:

NODE_ENV=production
VUE_APP_BUILD_TYPE=uat

模式优化后

这样子顺眼些了,但整个项目打包这部分还是有很大的优化空间,下期再干咯~

查看原文

赞 1 收藏 1 评论 2

认证与成就

  • 获得 25 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-03-17
个人主页被 1.1k 人浏览