欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談react 同構(gòu)之樣式直出

 更新時間:2017年11月07日 08:59:36   作者:devlee  
這篇文章主要介紹了淺談react 同構(gòu)之樣式直出,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言

上文講到通過同構(gòu)服務(wù)端渲染,可以直出html結(jié)構(gòu),雖然講解了樣式,圖片等靜態(tài)資源在服務(wù)端引入問題的解決方案,但是并沒有實際進行相關(guān)操作,這篇文章就講解一下如何讓樣式像html一樣直出。

PS: 直出,我的理解就是輸入url發(fā)起get請求訪問服務(wù)端,直接得到完整響應(yīng)結(jié)果,而不是同過ajax異步去獲取。

React 同構(gòu)的關(guān)鍵要素

完善的 Compponent 屬性及生命周期與客戶端的 render 時機是 React 同構(gòu)的關(guān)鍵。

DOM 的一致性

在前后端渲染相同的 Compponent,將輸出一致的 Dom 結(jié)構(gòu)。

不同的生命周期

在服務(wù)端上 Component 生命周期只會到 componentWillMount,客戶端則是完整的。

客戶端 render 時機

同構(gòu)時,服務(wù)端結(jié)合數(shù)據(jù)將 Component 渲染成完整的 HTML 字符串并將數(shù)據(jù)狀態(tài)返回給客戶端,客戶端會判斷是否可以直接使用或需要重新掛載。

以上便是 React 在同構(gòu)/服務(wù)端渲染的提供的基礎(chǔ)條件。在實際項目應(yīng)用中,還需要考慮其他邊角問題,例如服務(wù)器端沒有 window 對象,需要做不同處理等。下面將通過在手Q家校群上的具體實踐,分享一些同構(gòu)的 Tips 及優(yōu)化成果

加入樣式文件

目前我們的項目中還不存在任何樣式文件,所以需要先寫一個,就給組件App寫一個樣式文件吧。

安裝依賴

下面這些依賴都是后續(xù)會用到的,先安裝一下,下面會詳細講解每個依賴的作用。

復(fù)制代碼 代碼如下:

npm install postcss-loader postcss-import postcss-cssnext postcss-nested postcss-functions css-loader style-loader isomorphic-style-loader --save-dev

創(chuàng)建.pcss文件

css文件的后綴是.css,less文件的后綴是.less,這里我選擇使用PostCSS配合其插件來寫樣式,所以我就自己定義一個后綴.pcss好了。

// ./src/client/component/app/style.pcss

.root {
 color: red;
}

設(shè)定一個root類,樣式就是簡單的設(shè)置顏色為紅色。然后在App組件里引用它。

// ./src/client/component/app/index.tsx

...
import * as styles from './style.pcss';
...
 public render() {
  return (
   <div className={styles.root}>hello world</div>
  );
 }
...

這個時候你會發(fā)現(xiàn)編輯器里是這樣的:

出現(xiàn)這個問題是因為ts不知道這種模塊的類型定義,所以我們需要手動加入自定義模塊類型定義。在項目根目錄下新建@types文件夾,在此目錄下建立index.d.ts文件:

// ./@types/index.d.ts

declare module '*.pcss' {
 const content: any;
 export = content;
}

保存之后就不會看到編輯器報錯了,但是terminal里webpack打包會提示出錯,因為我們還沒有加對應(yīng)的loader。

配置.pcss文件的解析規(guī)則

js都組件化了,css模塊化也是很有必要的,不用再為避免取重復(fù)類名而煩惱。我們在base配置里新導(dǎo)出一個方法用以獲取postcss的規(guī)則。

// ./src/webpack/base.ts

...
export const getPostCssRule = (styleLoader) => ({
 test: /\.pcss$/,
 use: [
  styleLoader,
  {
   loader: 'css-loader',
   options: {
    camelCase: true,
    importLoaders: 1,
    localIdentName: '[path][name]---[local]---[hash:base64:5]',
    modules: true,
   },
  },
  {
   loader: 'postcss-loader',
   options: {
    plugins: () => [
     require('postcss-import')({
      path: path.join(baseDir, './src/client/style'),
     }),
     require('postcss-cssnext'),
     require('postcss-nested'),
     require('postcss-functions')({
      functions: {
       x2(v, u) {
        return v * 2 + (u ? u : 'px');
       },
      },
     }),
    ],
   },
  },
 ],
});
...

我們可以從上面這個方法看到,要處理 .pcss 文件需要用到三個loader,按處理順序從下往上分別是postcss-loader, css-loader, 還有一個變量styleLoader,至于這個變量是什么,我們可以看使用到該方法的地方:

// ./src/webpack/client.ts

...
(clientDevConfig.module as webpack.NewModule).rules.push(
 ...
 getPostCssRule({
  loader: 'style-loader',
 }),
 ...
);
...

// ./src/webpack/server.ts

...
(clientDevConfig.module as webpack.NewModule).rules.push(
 ...
 getPostCssRule({
  loader: 'isomorphic-style-loader',
 }),
 ...
);
...

客戶端和服務(wù)端處理樣式文件需要使用到不同的styleLoader。

PostCSS簡介

PostCSS是一個使用js來轉(zhuǎn)換css的工具,這個是官方介紹。其配合webpack使用的loader就是postcss-loader,但是只有單個postcss-loader其實沒有什么用,需要配合其插件來實現(xiàn)強大的功能。

1、postcss-import

這個插件我這里使用的原因是為了在樣式文件中@import時避免復(fù)雜的路徑編寫,我設(shè)定好path值,那么我在其它任何層級下的樣式文件中要引入path對應(yīng)文件夾里的公共變量樣式文件(假設(shè)叫"variables.pcss")時就非常方便,只需要寫import 'variables.pcss';就可以了,當(dāng)然如果找不到對應(yīng)的文件,它會忽略path使用默認相對路徑來查找。

2、postcss-cssnext

這個插件可以使用下一代css語法。

3、postcss-nested

這個插件可以嵌套編寫樣式。

4、postcss-functions

這個插件可以自定義函數(shù),并在樣式文件中調(diào)用。

講這么多,寫代碼舉個栗子吧~

我們在client目錄下新增style文件夾,用于存放一些樣式reset,變量文件之類的東西。然后創(chuàng)建兩個pcss文件:

// ./src/client/style/variables.pcss

:root {
 --fontSizeValue: 16;
}
// ./src/client/style/index.pcss

@import 'variables.pcss';

body {
 margin: 0;
 font-size: x2(var(--fontSizeValue));
}

引入我們剛寫的index.pcss

// ./src/client/index.tsx
...
import './style/index.pcss';
...

CSS Modules簡介

簡單來說就是css模塊化,不用再擔(dān)心全局類名的問題。我們根據(jù)上述css-loader的options來看:

  1. camelCase為true運行使用駝峰寫法來寫類名
  2. importLoaders的值為N是因為在css-loader之前有N個loader已經(jīng)處理過文件了,這里的N值是1,因為之前有一個postcss-loader,這個值一定要設(shè)置對,否則會影響@import語句,我的這個表述可能不是太正確,詳細可參見 Clarify importLoaders documentation? 這個地方詳細講解了,我翻譯一下大概意思是,這個屬性的值N代表的是對于@import的文件要經(jīng)過css-loader后面的N個loader的處理,英文不太好,大家可以自行理解。
  3. localIdentName這個就是指生成的類名啦,具體看后續(xù)結(jié)果截圖就一目了然了。
  4. modules為true即啟用模塊化

isomorphic-style-loader

在客戶端,使用style-loader,它會動態(tài)的往dom里插入style元素,而服務(wù)端由于缺少客戶端的相關(guān)對象及API,所以需要isomorphic-style-loader,目前用到它只是為了避免報錯哈哈,后續(xù)還有大作用,樣式直出全靠它。

打包運行

注意:打包運行之前不要忘了給tsconfig.client.json和tsconfig.server.json引入我們的自定義模塊定義文件index.d.ts,不然webpack編譯就會報找不到pcss這種模塊啦。

// ./src/webpack/tsconfig.client(server).json
...
"include": [
  ...
  "../../@types/**/*",
  ...
]
...

運行結(jié)果如下:

雖然style元素已經(jīng)存在,但是這個是由style-loader生成的,并不是服務(wù)端直出的,看page source就知道了。

而且在刷新頁面的時候能很明顯的看到樣式變化閃爍的效果。

直出樣式

我們利用isomorphic-style-loader來實現(xiàn)服務(wù)端直出樣式,原理的話根據(jù)官方介紹就是利用了react的context api來實現(xiàn),在服務(wù)端渲染的過程中,利用注入的insertCss方法和高階組件(hoc high-order component)來獲取樣式代碼。

安裝依賴

npm install prop-types --save-dev

改寫App組件

根據(jù)其官方介紹,我們在不使用其整合完畢的isomorphic router的情況下,需要寫一個Provider給App組件:

// ./src/client/component/app/provider.tsx

import * as React from 'react';

import * as PropTypes from 'prop-types';

class AppProvider extends React.PureComponent<any, any> {
 public static propTypes = {
  context: PropTypes.object,
 };

 public static defaultProps = {
  context: {
   insertCss: () => '',
  },
 };

 public static childContextTypes = {
  insertCss: PropTypes.func.isRequired,
 };

 public getChildContext() {
  return this.props.context;
 }

 public render() {
  return this.props.children || null;
 }
}

export default AppProvider;

將原App組件里的具體內(nèi)容遷移到AppContent組件里去:

// ./src/client/component/app/content.tsx

import * as React from 'react';

import * as styles from './style.pcss';

/* tslint:disable-next-line no-submodule-imports */
import withStyles from 'isomorphic-style-loader/lib/withStyles';

@withStyles(styles)
class AppContent extends React.PureComponent {
 public render() {
  return (
   <div className={styles.root}>hello world</div>
  );
 }
}

export default AppContent;

新的App組件:

// ./src/client/component/app/index.tsx

import * as React from 'react';

import AppProvider from './provider';

import AppContent from './content';

class App extends React.PureComponent {
 public render() {
  return (
   <AppProvider>
    <AppContent />
   </AppProvider>
  );
 }
}

export default App;

疑問一:AppProvider組件是做什么的?

答:Provider的意思是 供應(yīng)者,提供者 。顧名思義,AppProvider為其后代組件提供了一些東西,這個東西就是context,它有一個insertCss方法。根據(jù)其定義,該方法擁有默認值,返回空字符串的函數(shù),即默認沒什么作用,但是可以通過props傳入context來達到自定義的目的。通過設(shè)定childContextTypes和getChildContext,該組件后代凡是設(shè)定了contextTypes的組件都會擁有this.context對象,而這個對象正是getChildContext的返回值。

疑問二:AppContent為何要獨立出去?

答:接上一疑問,AppProvider組件render其子組件,而要使得context這個api生效,其子組件必須是定義了contextTypes的,但是我們并沒有看見AppContent有這個定義,這個是因為這個定義在高階組件withStyles里面(參見其 源碼 )。

疑問三:@withStyles是什么語法?

答:這個是裝飾器,屬于es7。使用該語法,需要配置tsconfig:

// ./tsconfig.json
// ./src/webpack/tsconfig.client(server).json

{
 ...
 "compilerOptions": {
  ...
  "experimentalDecorators": true,
  ...
 },
 ...
}

改寫服務(wù)端bundle文件

由于App組件的改寫,服務(wù)端不能再復(fù)用該組件,但是AppProvider和AppContent目前還是可以復(fù)用的。

// ./src/server/bundle.tsx

import * as React from 'react';

/* tslint:disable-next-line no-submodule-imports */
import { renderToString } from 'react-dom/server';

import AppProvider from '../client/component/app/provider';

import AppContent from '../client/component/app/content';

export default {
 render() {
  const css = [];
  const context = { insertCss: (...styles) => styles.forEach((s) => css.push(s._getCss())) };
  const html = renderToString(
   <AppProvider context={context}>
    <AppContent />
   </AppProvider>,
  );
  const style = css.join('');
  return {
   html,
   style,
  };
 },
};

這里我們傳入了自定義的context對象,通過css這個變量來存儲style信息。我們原先render函數(shù)直接返回renderToString的html字符串,而現(xiàn)在多了一個style,所以我們返回擁有html和style屬性的對象。

疑問四:官方示例css是一個Set類型實例,這里怎么是一個數(shù)組類型實例?

答:Set是es6中新的數(shù)據(jù)結(jié)構(gòu),類似數(shù)組,但可以保證無重復(fù)值,只有tsconfig的編譯選項中的target為es6時,且加入es2017的lib時才不會報錯,由于我們的target是es5,所以是數(shù)組,且使用數(shù)組并沒有太大問題。

處理服務(wù)端入口文件

由于bundle的render值變更,所以我們也要處理一下。

// ./src/server/index.tsx

...
router.get('/*', (ctx: Koa.Context, next) => { // 配置一個簡單的get通配路由
 const renderResult = bundle ? bundle.render() : {}; // 獲得渲染出的結(jié)果對象
 const { html = '', style = '' } = renderResult;
 ...
 ctx.body = `
  ...
  <head>
   ...
   ${style ? `<style>${style}</style>` : ''}
   ...
  </head>
  ...
 `;
 ...
});
...

直出結(jié)果

樣式直出后的page source:

找回丟失的公共樣式文件

從上面的直出結(jié)果來看,缺少./src/style/index.pcss這個樣式代碼,原因顯而易見,它不屬于任何一個組件,它是公共的,我們在客戶端入口文件里引入了它。對于公共樣式文件,服務(wù)端要直出這部分內(nèi)容,可以這么做:

./src/server/bundle.tsx

...
import * as commonStyles from '../client/style/index.pcss';
...
const css = [commonStyles._getCss()];
...

我們利用isomorphic-style-loader提供的api可以得到這部分樣式代碼字符串。這樣就可以得到完整的直出樣式了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • VSCode搭建React Native環(huán)境

    VSCode搭建React Native環(huán)境

    這篇文章主要介紹了VSCode搭建React Native環(huán)境,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容

    React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容

    這篇文章主要介紹了React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • React?Immutable使用方法詳細介紹

    React?Immutable使用方法詳細介紹

    Immutable.js出自Facebook,是最流行的不可變數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)之一。它實現(xiàn)了完全的持久化數(shù)據(jù)結(jié)構(gòu),使用結(jié)構(gòu)共享。所有的更新操作都會返回新的值,但是在內(nèi)部結(jié)構(gòu)是共享的,來減少內(nèi)存占用
    2022-09-09
  • react?context優(yōu)化四重奏教程示例

    react?context優(yōu)化四重奏教程示例

    這篇文章主要為大家介紹了react?context優(yōu)化四重奏教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • React深入淺出分析Hooks源碼

    React深入淺出分析Hooks源碼

    在react類組件(class)寫法中,有setState和生命周期對狀態(tài)進行管理,但是在函數(shù)組件中不存在這些,故引入hooks(版本:>=16.8),使開發(fā)者在非class的情況下使用更多react特性
    2022-11-11
  • react-native 配置@符號絕對路徑配置和絕對路徑?jīng)]有提示的問題

    react-native 配置@符號絕對路徑配置和絕對路徑?jīng)]有提示的問題

    本文主要介紹了react-native 配置@符號絕對路徑配置和絕對路徑?jīng)]有提示的問題,文中通過圖文示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • 在create-react-app中使用css modules的示例代碼

    在create-react-app中使用css modules的示例代碼

    這篇文章主要介紹了在create-react-app中使用css modules的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • 基于React.js實現(xiàn)簡單的文字跑馬燈效果

    基于React.js實現(xiàn)簡單的文字跑馬燈效果

    剛好手上有一個要實現(xiàn)文字跑馬燈的react項目,然后ant-design上面沒有這個組件,于是只能自己手擼一個,文中的實現(xiàn)方法講解詳細,希望對大家有所幫助
    2023-01-01
  • React實現(xiàn)多標(biāo)簽在有限空間內(nèi)展示

    React實現(xiàn)多標(biāo)簽在有限空間內(nèi)展示

    在業(yè)務(wù)中,需要在一個卡片組件中展示多個標(biāo)簽,標(biāo)簽組件高度相同,寬度和出現(xiàn)順序不同,要求標(biāo)簽只能在有限的空間內(nèi)展示,所以本文給大家介紹了React實現(xiàn)多標(biāo)簽在有限空間內(nèi)展示,需要的朋友可以參考下
    2023-12-12
  • react-native滑動吸頂效果的實現(xiàn)過程

    react-native滑動吸頂效果的實現(xiàn)過程

    這篇文章主要給大家介紹了關(guān)于react-native滑動吸頂效果的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用react-native具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06

最新評論