您当前的位置:主页 > 国内 >

作者:成建顺 来源:原创 发布日期:10-14

从零开始配置TypeScript + React + React-Router + Redux + Webpack开发环境

转载请注明出处!

说在前面的话:

1、为什么不使用现成的脚手架?脚手架配置的东西太多太重了,一股脑全塞给你,我只想先用一些我能懂的库和插件,然后慢慢的添加其他的。而且自己从零开始配置也能学到更多的东西不是么。

2、教程只配置了开发环境,并没有配置生产环境。

3、教程针对人群是有过React + Redux经验,并且想在新项目中使用TypeScript的人(或者是想自己从零开始配置开发环境的)

4、因为前端发展日新月异,今天能用的配置到明天可能就不能用了(比如React-Router就有V4了,而且官方说是完全重写的),所以本文中安装的包都是指定版本的。

5、教程遵循最小可用原则,所以能不用的库和插件就没用(主要是会的就没多少,怕用出问题,逃~~)。

6、基于5,所以教程不会一开始就把所有东西全装上,会一步一步慢慢来。

6、教程在macOS下完成,win环境系可能会有一些其他的问题。

初始环境

node版本为6.9.0

初始化项目

创建并进入项目

mkdir demo && cd demo

初始化项目

npm init

安装初始依赖

首先是安装webpack和webpack-dev-server(全局安装过的请忽略)

npm i -D webpack@3.6.0

然后安装React和Types中React的声明文件

npm i --S react@15.5.4 react-dom@15.5.4 @types/react@15.6.0 @types/react-dom@15.5.0

上面@types开头的包都是typeScript的声明文件,你可以进入node_modules/@types/XX/index.d.ts进行查看。

关于声明文件的具体介绍可以在github上的DefinitelyTyped库看到。

接下来安装TypeScript,ts-loader和source-map-loader

npm i -D typescript@2.5.3 ts-loader@2.3.7 source-map-loader@0.2.2

ts-loader可以让Webpack使用TypeScript的标准配置文件tsconfig.json编译TypeScript代码。

source-map-loader使用任意来自Typescript的sourcemap输出,以此通知webpack何时生成自己的sourcemaps。 这让你在调试最终生成的文件时就好像在调试TypeScript源码一样。

添加TypeScript配置文件

我们需要一个tsconfig.json文件来告诉ts-loader如何编译代码TypeScript代码。

在当前根目录下创建tsconfig.json文件,并添加如下内容:

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react"
  },
  "include": [
    "./src/**/*"
  ]
}

outDir:输出目录。

sourceMap:把 ts 文件编译成 js 文件的时候,同时生成对应的sourceMap文件。

noImplicitAny:如果为true的话,TypeScript 编译器无法推断出类型时,它仍然会生成 JavaScript 文件,但是它也会报告一个错误。为了找到错误还是设置为true比较好。

module:代码规范,也可以选AMD,CMD。

target:转换成es5

jsx:TypeScript具有三种JSX模式:preserve,reactreact-native。 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在preserve模式下生成代码中会保留JSX以供后续的转换操作使用(比如:Babel)。 另外,输出文件会带有.jsx扩展名。react模式会生成React.createElement,在使用前不需要再进行转换操作了,输出文件的扩展名为.js。react-native相当于preserve,它也保留了所有的JSX,但是输出文件的扩展名是.js。我们这里因为不会用babel再转,所以用react就行。

include:需要编译的目录。

写些代码

首先创建目录

mkdir src && cd src
mkdir components && cd components

在此文件夹下添加一个Hello.tsx文件,代码如下:

import * as React from "react";

export interface Props {
  name: string;
  enthusiasmLevel?: number;
}

export default class Hello extends React.Component<Props, object> {
  render() {
    const { name, enthusiasmLevel = 1 } = this.props;

    if (enthusiasmLevel <= 0) {
      throw new Error("You could be a little more enthusiastic. :D");
    }

    return (
      <div className="hello">
        <div className="greeting">
          Hello {name + getExclamationMarks(enthusiasmLevel)}
        </div>
      </div>
    );
  }
}

function getExclamationMarks(numChars: number) {
  return Array(numChars + 1).join("!");
}

接下来,在src下创建index.tsx文件,代码如下:

import * as React from "react";
import * as ReactDOM from "react-dom";

import Hello from "./components/Hello";

ReactDOM.render(
  <Hello name="TypeScript" enthusiasmLevel={10} />,
  document.getElementById("root") as HTMLElement
);

我们还需要一个页面来显示Hello组件。 在根目录创建一个名为index.html的文件,如下:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8" />
  <title>demo</title>
</head>

<body>
  <div id="root"></div>
  <script src="./dist/bundle.js"></script>
</body>

</html>

编写webpack配置文件

在根目录下创建一个名为webpack.common.config.js文件,并添加一下内容:

module.exports = {
  entry: "./src/index.tsx",
  output: {
    filename: "bundle.js",
    path: __dirname + "/dist"
  },

  devtool: "source-map",

  resolve: {
    extensions: [".ts", ".tsx", ".js", ".json"]
  },

  module: {
    rules: [
      { test: /.tsx?$/, loader: "ts-loader" },

      { enforce: "pre", test: /.js$/, loader: "source-map-loader" }
    ]
  },

  plugins: [
  ],
};

这里不做过多的解释了。基本上有webpack经验的都看得懂。至于为什么是webpack.common.config.js而不是webpack.config.js。是因为我们现在要配置的是开发环境,以后需要配置生产环境,所以我们就需要多个配置文件,并且将这两个的通用部分放入到webpack.common.config.js

在根目录下运行一下命令:

webpack --config webpack.common.config.js

然后打开index.html就能看到我们写的页面了。

编写webpack开发环境配置文件

如果是正式做开发,上面的代码肯定是不够的,我们需要webpacl-dev-server提供的最基本也是最好用的热更新功能。

npm i -D webpack-dev-server@2.9.1

在根目录下创建webpack.dev.config.js,并添加以下配置:

const webpack = require("webpack");
const config = require("./webpack.common.config");
config.devServer = {
  hot: true,
  publicPath: "/dist/"
}
config.plugins.push(new webpack.HotModuleReplacementPlugin());
module.exports = config;

首先需要引入公共的配置,然后在基础之上进行修改。

devServer就是webpack-dev-server的配置项。

hot:开启热更新,开启热更新之后,我们需要在plugins中加入webpack.HotModuleReplacementPlugin来完全启用。同时官方文档中指出,如果在命令中使用--hot来启动webpack-dev-server的话,就会自动加载这个插件,不再需要在config.js中进行引入。

关于HMR的相关部分可以点击webpack HMR查看。

publicPath:资源目录,因为webpack-dev-server启动之后会把编译后的资源放在内存当中,那这些资源在哪呢?就是在publicPath指定的目录里,因为我们在webpack.common.config.js中配置的output.path是当前目录的/dist目录下,为了不再去更改根目录下的index.html文件,所以我们这里也设置成/dist/。 这部分内容具体可以参照详解Webpack2的那些路径

运行命令:

webpack-dev-server --config webpack.dev.config.js

打开网页,进入localhots:8080就可以看到我们的页面了。打开浏览器的开发者工具,在console部分能看到以下两句提示就说明热更新启动成功了,

然后把这部分很长的命令加入到npm scripts。在package.json的scripts下添加"start": "webpack-dev-server --config webpack.dev.config.js"

输入npm start 就可以开启我们的服务了。

添加一个简单的redux(非新手向)

安装redux的依赖

npm i -S redux@3.7.2 react-redux@5.0.5 @types/react-redux@5.0.6

为了能体现redux,我们接下来给我们的网页添加两个按钮来增加/删除文字后面的感叹号。

首先,我们来创建一个文件来存放store的接口声明,放入src/types/index.tsx中,代码如下:

export interface StoreState {
    languageName: string;
    enthusiasmLevel?: number;
}

定义一些常量供action和reducer使用,放入src/constants/index.tsx

export const INCREMENT_ENTHUSIASM = "INCREMENT_ENTHUSIASM";
export type INCREMENT_ENTHUSIASM = typeof INCREMENT_ENTHUSIASM;


export 旅游资讯网const DECREMENT_ENTHUSIASM = "DECREMENT_ENTHUSIASM";
export type DECREMENT_ENTHUSIASM = typeof DECREMENT_ENTHUSIASM;

添加action,放入src/actions/index.tsx

import * as constants from "../constants"

export interface IncrementEnthusiasm {
  type: constants.INCREMENT_ENTHUSIASM;
}

export interface DecrementEnthusiasm {
  type: constants.DECREMENT_ENTHUSIASM;
}

export type EnthusiasmAction = IncrementEnthusiasm | DecrementEnthusiasm;

export function incrementEnthusiasm(): IncrementEnthusiasm {
  return {
    type: constants.INCREMENT_ENTHUSIASM
  }
}

export function decrementEnthusiasm(): DecrementEnthusiasm {
  return {
    type: constants.DECREMENT_ENTHUSIASM
  }
}

添加reducer,放入src/reducers/index.tsx

import { EnthusiasmAction } from "../actions";
import { StoreState } from "../types/index";
import { INCREMENT_ENTHUSIASM, DECREMENT_ENTHUSIASM } from "../constants/index";

export function enthusiasm(state: StoreState, action: EnthusiasmAction): StoreState {
  switch (action.type) {
    case INCREMENT_ENTHUSIASM:
      return { ...state, enthusiasmLevel: state.enthusiasmLevel + 1 };
    case DECREMENT_ENTHUSIASM:
      return { ...state, enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1) };
  }
  return state;
}

修改一下Hello组件,以下是修改后的代码:

import * as React from "react";

export interface Props {
  name: string;
  enthusiasmLevel?: number;
  onI旅游资讯网ncrement?: () => void;
  onDecrement?: () => void;
}

export default function Hello({ name, enthusiasmLevel = 1, onIncrement, onDecrement }: Props) {
  if (enthusiasmLevel <= 0) {
    throw new Error("You could be a little more enthusiastic. :D");
  }

  return (
    <div className="hello">
      <div className="greeting">
        Hello {name + getExclamationMarks(enthusiasmLevel)}
      </div>
      <div>
        <button onClick={onDecrement}>-</button>
        <button onClick={onIncrement}>+</button>
      </div>
    </div>
  );
}

function getExclamationMarks(numChars: number) {
  return Array(numChars + 1).join("!");
}

此时我们页面已经修改成功了,但点击没有反应,因为我们还没有连接到redux的store中。

添加一个container来链接Hello组件,放入src/containers/Hello.tsx中

import Hello from "../components/Hello";
import * as actions from "../actions/";
import { StoreState } from "../types/index";
import { connect, Dispatch } from "react-redux";

export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) {
  return {
    enthusiasmLevel,
    name: languageName,
  }
}

export function mapDispatchToProps(dispatch: Dispatch<actions.EnthusiasmAction>) {
  return {
    onIncrement: () => dispatch(actions.incrementEnthusiasm()),
    onDecrement: () => dispatch(actions.decrementEnthusiasm()),
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Hello);

创建一个initState,来定义store初始的值,放入/src/store/initState.tsx中

export default {
  enthusiasmLevel: 1,
  languageName: "TypeScript",
}

创建一个store,放入/src/store/configureStore.tsx中

import { createStore } from "redux";
import { enthusiasm } from "../reducers/index";
import { StoreState } from "../types/index";
import initState from "./initState";
export default function () {
  const store = createStore<StoreState>(enthusiasm, initState);
  return store;
}

最后修改一下入口文件index.tsx

import * as React from "react";
import * as ReactDOM from "react-dom";
import Hello from "./containers/Hello";
import { Provider } from "react-redux";
import configureStore from "./store/configureStore";

const store = configureStore();
ReactDOM.render(
  <Provider store={store}>
    <Hello />
  </Provider>,
  document.getElementById("root") as HTMLElement
);

至此,一个简单的redux就弄好了。可以点击按钮增加/删除感叹号了。

但是现在还有很多不完善的地方,比如Hello组件竟然是一个函数,再比如reducer竟然只有一个(解决这两个问题的过程中会有一些bug待我们解决)。

放心,这些都将在下面的“添加一个够用的Redux”中解决。

添加一个够用的Redux

很明显,一个简单的redux在我们稍微大一点的开发中是明显不够用的。

所以我们来改写一下我们的代码。

首当其冲的就是我们的Hello组件。我们把Hello组件改成class的形式

export default class Hello extends React.Component<Props, {}> {
  constructor(props: Props) {
    super(props);
  }
  render() {
    const { name, enthusiasmLevel = 1, onIncrement, onDecrement } = this.props;

    if (enthusiasmLevel <= 0) {
      throw new Error("You could be a little more enthusiastic. :D");
    }

    return (
      <div className="hello">
        <div className="greeting">
          Hello {name + getExclamationMarks(enthusiasmLevel)}
        </div>
        <div>
          <button onClick={onDecrement}>-</button>
          <button onClick={onIncrement}>+</button>
        </div>
      </div>
    );
  }
}

保存,编译中,然后就报错了!

------------------------------------------------------------------------------------------------

ERROR in ./src/containers/Hello.tsx
(20,61): error TS2345: Argument of type "typeof Hello" is not assignable to parameter of type "ComponentType<{ enthusiasmLevel: number; name: string; } & { onIncrement: () => IncrementEnthusia...".
Type "typeof Hello" is not assignable to type "StatelessComponent<{ enthusiasmLevel: number; name: string; } & { onIncrement: () => IncrementEnt...".
Type "typeof Hello" provides no match for the signature "(props: { enthusiasmLevel: number; name: string; } & { onIncrement: () => IncrementEnthusiasm; onDecrement: () => DecrementEnthusiasm; } & { children?: ReactNode; }, context?: any): ReactElement<any>".

------------------------旅游资讯网------------------------------------------------------------------------

赶紧复制,然后google一下,就能找到我们要的答案TypeScript-React-Starter | Issues#29,从别人的回答来看貌似是一个bug?那我们就按回答来更改一下我们的Hello容器

export function mergeProps(stateProps: Object, dispatchProps: Object, ownProps: Object) {
  return Object.assign({}, ownProps, stateProps, dispatchProps);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps)(Hello);

刚改完,还没等保存,IDE就提醒我们有一个错误:

Property "assign" does not exist on type "ObjectConstructor".

很明显,是因为Object没有assign这个方法,有两种解决方式,一种是安装Object-assign这个npm包,用这个包去替代。另一种是在tsconfig.json中把target从"es5"修改为"es6"。

然后再次编译,发现还是依旧报错。只不过这次错误信息改了:

------------------------------------------------------------------------------------------------

ERROR in ./src/index.tsx
(10,5): error TS2322: Type "{}" is not assignable to type "IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<Props, "name" | "enthusiasmLevel" |...".
Type "{}" is not assignable to type "Readonly<Pick<Props, "name" | "enthusiasmLevel" | "onIncrement" | "onDecrement"> & Object>".
Property "name" is missing in type "{}".

------------------------------------------------------------------------------------------------

这次的报错是在index.tsx中,可以看到是因为在Hello组件我们定义的接口中name的属性是必须传的,但是在index.tsx中没有显示的传过去。

但是如果你的浏览器是chrome并安装了react插件的话可以看到编译后的代码是有传的

姑且就当做是一个bug吧,解决方案有两种,一种是的index.tsx中给Hello容器加上一个name

ReactDOM.render(
  <Provider store={store}>
    <Hello name="123"/>
  </Provider>,
  document.getElementById("root") as HTMLElement
);

这里就算加上name也还是直接显示的store中的name。所以我们这里采用这种方式,并且后面加上React-router之后这段代码就会改掉,就不会有这个问题了。

另一种是在Hello组件中把name属性改成非必要属性:

export interface Props {
  name?: string;
  enthusiasmLevel?: number;
  onIncrement?: () => void;
  onDecrement?: () => void;
}

这种方式不推荐。

好了,到现在为止组件更改完成了。

接下来我们解决多个reducer的问题。

首先我们把initState的默认值更改一下:

export default {
  demo: {
    enthusiasmLevel: 1,
    languageName: "TypeScript",
  }
}

当然还有/src/types/index.tsx也要更改:

export interface demo {
  languageName: string;
  enthusiasmLevel?: number;
}
export interface StoreState {
  demo: demo;
}

然后讲/src/reducers/index.tsx命名为demo.tsx,并对内容进行修改:

import { EnthusiasmAction } from "../actions";
import { demo } from "../types/index";
import { INCREMENT_ENTHUSIASM, DECREMENT_ENTHUSIASM } from "../constants/index";
import initState from "../store/initState";
export function enthusiasm(state: demo = initState.demo, action: EnthusiasmAction): demo {
  switch (action.type) {
    case INCREMENT_ENTHUSIASM:
      return { ...state, enthusiasmLevel: state.enthusiasmLevel + 1 };
    case DECREMENT_ENTHUSIASM:
      return { ...state, enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1) };
  }
  return state;
}

其实就是把对应的接口类型进行了更改,并给state添加了默认值。

然后新建一个index.tsx文件,并添加以下内容:

import { combineReducers } from "redux";
import { enthusiasm } from "./demo";
const rootReducer = combineReducers({
  demo: enthusiasm
});

export default rootReducer;

相对应的,也需要修改Hello容器中的引用的值:

export function mapStateToProps({ demo: { enthusiasmLevel, languageName } }: StoreState) {
  return {
    enthusiasmLevel,
    name: languageName,
  }
}

最后修改一下configureStore中的引用的reducer:

import { createStore } from "redux";
import  reducers  from "../reducers/index";
import { StoreState } from "../types/index";
import initState from "./initState";
export default function () {
  const store = createStore<StoreState>(reducers, initState);
  return store;
}

更改完毕,保存。报错...

------------------------------------------------------------------------------------

ERROR in ./src/s旅游资讯网tore/configureStore.tsx
(6,41): error TS2345: Argument of type "Reducer<{}>" is not assignable to parameter of type "Reducer<StoreState>".
Type "{}" is not assignable to type "StoreState".
Property "demo" is missing in type "{}".

------------------------------------------------------------------------------------

是不是感觉很熟悉,和之前index.tsx中关于Hello组件的报错几乎一样。

这里也有两种解决方案,一种是找到configureStore.tsx中的const store = createStore<StoreState>(reducers, initState);<StoreState>泛型删除。

第二种是和之前一样的,找到/src/types/index.tsx,将demo: demo;加上一个?使之变为非必须的属性demo?: demo;我们这里就采用这种方法。

这里究竟是bug还是其他什么原因,希望有大神能解答。

至此,我们够用的redux就已经完成了。

添加一个React-Router

需要注意的是,现在react-router已经到了V4版本了,并且官方说这是一个完全重写的版本。所以在我不太熟悉的情况下,保险起见还是先选择V3版本,等以后再更新。

安装依赖

npm i -S react-router@3.0.5 @types/react-router@3.0.5

在src目录下创建文件routers.tsx,并添加以下内容:

import * as React from "react";
import { Route, IndexRoute } from "react-router";
import Hello from "./containers/Hello";

export default (
  <Route path="/">
    <IndexRoute component={Hell旅游资讯网o} />
    <Route path="/demo">
      <IndexRoute component={Hello} />
    </Route>
  </Route>
);

为了显示路由的作用,就加了一个demo路径。

然后在index.tsx中加上我们的路由

import * as React from "react";
import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import configureStore from "./store/configureStore";
import { Router, browserHistory } from "react-router";
import routes from "./routes";
const store = configureStore();
ReactDOM.render(
  <Provider store={store}>
    <Router history={browserHistory} routes={routes} />
  </Provider>,
  document.getElementById("root") as HTMLElement
);

由于我们添加的是browserHistory作为路由,不是hashHistory,所以我们需要对服务器做一些路由配置才行。至于为什么,请自行搜索,这里不做说明了。如果不想用过多设置,也可以直接把browserHistory替换为hashHistory即可。

这里我们的开发服务器就是webpack-dev-server,所以我们对webpack.dev.congfig.js进行更改:

const webpack = require("webpack");
const config = require("./webpack.common.config");
config.devServer = {
  hot: true,
  publicPath: "/dist/",
  historyApiFallback: {
    index: "./index.html"
  },
}
config.plugins.push(new webpack.HotModuleReplacementPlugin());
module.exports = config;

其实就是当服务器找不到路由目录时将页面指向index.html即可。

因为更改了配置,所以我们需要重启服务器npm start

进入localhost:8080/demo

页面有显示Hello组件,说明配置成功了。

添加React-Router-Redux

这里同样由于React-Router版本大更新的问题,所以也要严格控制版本。

安装依赖

npm i -S react-router-redux@4.0.8 @types/react-router-redux@4.0.48

更改index.tsx代码如下:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import configureStore from "./store/configureStore";
import { Router, browserHistory } from "react-router";
import routes from "./routes";
import { syncHistoryWithStore } from "react-router-redux";

const store = configureStore();
const history = syncHistoryWithStore(browserHistory, store);

ReactDOM.render(
  <Provider store={store}>
    <Router history={history} routes={routes} />
  </Provider>,
  document.getElementById("root") as HTMLElement
);

然后在src/reducers/index.tsx中添加上routerReducer

import { combineReducers } from "redux";
import { routerReducer } from "react-router-redux";
import { enthusiasm } from "./demo";
const rootReducer = combineReducers({
  demo: enthusiasm,
  routing: routerReducer
});

export default rootReducer;

OK,非常简单。

结束语

如果在React-Router和React-Router-Redux的配置中有什么报错,基本上是npm包的版本问题,请删除node_modules并按照我指定的版本重新安装。

总结:安装过程中确实碰到了各种各样的问题,尤其是Router的包附带的history版本问题,弄了很久。看似很简单的教程,背后有我踩过无数的坑,不过好在还是完成了。

之后还要继续集成ant-design,以及生产环境的旅游资讯网配置,这些都将会在本教程继续更新。

参考资料:TypeScript-React-Starter

React与webpack | TypeScript Handbook(中文版)

webpack HMR

详解Webpack2的那些路径

TypeScript-React-Starter | Issues#29

webpack-dev-server使用react-router browserHistory的配置

当前文章:http://qivf.tengtengyuanyi.com/a/fe4c1_58.html

发布时间:2017-10-19 01:46:20

星二代不会搭地铁五笔  

Copyright @ 2016-2018 小米手机 版权所有
http://www.xinyidaishujuzhongxin.comhttp://olmcrf.cnhttp://irziqv.cnhttp://cowjbt.cnhttp://zxrxpz.cnhttp://jkjpvh.cnhttp://qsppfk.cnhttp://sxcou.comhttp://www.sengeLighting.comhttp://www.zcchatai.comhttp://www.wfkangLi.comhttps://lc.emintong.com/category/biaolidianhttp://www.baibangchina.comhttp://www.xianzhihulian.com/a/703f9_3862.htmlhttp://www.xiangfengyy.comhttp://www.noakjjianzhan.comhttp://xwfnetyyfk.comhttp://fhbsyyfk.comhttp://laxhrlyy.comhttp://hbxszsyy.comhttp://wzwtnkyy.comhttp://www.zhsydz.cnhttp://xunsq.cnhttp://xunsr.cnhttp://www.bdyishangkuaijie.comhttp://txzbud.cnhttp://www.xianzhihulian.com/5dtf538t/http://www.xianzhihulian.com/99h/http://www.xianzhihulian.com/8imm7/http://www.gaofangxievip.com/news/?web_6.htmlhttp://www.xianzhihulian.com/a/74d00_3342.htmlhttp://www.xianzhihulian.com/a/2e426_1792.htmlhttp://www.fspaken.comhttp://www.deruimoju.comhttp://hooedn.cn/sitemap.xmlhttp://www.sijieshengwu.comhttp://gycufh.cn/sitemap.xmlhttp://odmick.cn/sitemap.xmlhttp://yqcibr.cn/sitemap.xmlhttp://twlxs.cnhttp://hkmry.cnhttp://wwldc.cnhttp://wwlbg.cnhttp://wwkry.cnhttp://fsrzmc.comhttp://dwcsld.comhttp://pymyhr.comhttp://jsmjff.comhttp://wctzhb.comhttp://www.jhsdjj.com/a2i78/f2vey.html围网围栏南宁旧房翻新贵金属直播金融界赢天下贵金属直播间金易通贵金属直播室贵金属直播间喊单大连贵金属直播现货直播室哪个好华鼎原油直播赢投长江原油直播室原油直播eia原油直播间喊单天下原油直播原油直播室现货直播石油实盘直播什么值得买1080P贵金属直播间直播南宁旧房翻新牛栏网MBA包过班投资公司投资公司南宁装修公司查号吧查号吧投资公司南宁别墅装修南宁旧房翻新电动车电池百度搜索百度搜索百度搜索燃气发电机德国阳光官网喷砂机厂家镀钛混凝土搅拌站广州服务器租用麻辣烫加盟长沙搬家AST外汇微商鞋子货源 印度药物代购印度 吉三代 直邮采暖散热器猩球崛起3天津网站建设足球比分