Closed childrentime closed 3 years ago
我尝试修改了purgecss的代码
module.exports = function () {
return {
name: 'no-require-css',
visitor: {
ImportDeclaration(path: any, state: any) {
let importFile = path.node.source.value;
console.log(importFile);
if (importFile.indexOf('.css') > -1) {
path.remove();
}
if (importFile.indexOf('.less') > -1) {
path.remove();
}
if (importFile.indexOf('.png') > -1) {
path.remove();
}
//新增的
if (importFile === 'antd-mobile') {
path.remove();
}
}
}
};
};
ok 我可以成功访问到某些页面了 但是当我直接访问某个包含antd-mobile
组件的页面的时候 它会报错
ReferenceError: InfiniteScroll is not defined
所以我需要手动地去判断当前的环境是document还是node
有没有更好的方法呢
看起来是 InfiniteScroll
组件的引用没有找到,可以贴一下你的 babel 配置么?
@awmleer babel就配置在webpack中 没有.babelrc.js
文件
奇怪,看起来 babel 配置是没有问题的。
那你是怎么引入的 InfiniteScroll
这个组件的呢?
类似于这样 我现在是做了一个环境的判断
import { InfiniteScroll } from 'antd-mobile';
import produce from 'immer';
import { useState } from 'react';
import siteObj from '@/store/site';
import {
annoucementList,
AnnouncementList,
AnnouncementListParams
} from '@/api/announcement';
import './style.less';
const prefix = 'announcement';
export default function Announcement() {
const [hasMore, setHasMore] = useState(true);
const [offset, setOffset] = useState(0);
const [announcement, setAnnouncement] = useState<AnnouncementList>({
rows: [],
total: 0
});
const loadMore = async () => {
const data: AnnouncementListParams = {
site: siteObj.site,
limit: 5,
offset
};
const res = await annoucementList(data);
console.log(res.data);
const copy = produce(announcement, draft => {
draft.rows = draft.rows.concat(res.data.rows);
draft.total = res.data.total;
});
setHasMore(copy.rows.length !== copy.total);
setOffset(copy.rows.length);
setAnnouncement(copy);
};
if (typeof window !== 'undefined') {
return (
<div className={prefix}>
{announcement.rows.map(item => <RenderAnnouncement key={item._id} data={item} />)}
<InfiniteScroll loadMore={loadMore} hasMore={hasMore} />
</div>
);
// 服务端渲染的时候 我们应该返回的静态HTML
}
return <></>;
}
const RenderAnnouncement = (props: { data: AnnouncementList['rows'][0] }) => {
const { data } = props;
return (
<div
className={`${prefix}-container`}
onClick={() => {
window.open(data.link);
}}
>
<div className={`${prefix}-container-top`}>{data.title}</div>
<div className={`${prefix}-container-bottom`}>
<span>14分钟前</span>
<span>{data.user.nickname}</span>
</div>
</div>
);
};
你这样相当于没有SSR了
你这个问题和我遇到时一个问题,本质上还是antd-mobile里强制导入了CSS,导致Nodejs没法直接运行antd-mobile库里的代码。antd-mobile不能作为一个纯粹的外部库了,externals: [nodeExternals()]这需要将antd-mobile排除掉。
我排除了antd-mobile 并且增加了服务端处理css webpack.server.ts
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import path from 'path';
import nodeExternals from 'webpack-node-externals';
const noRequireCss = require('./purge-css');
//服务端打包
module.exports = {
mode: 'production',
entry: { index: path.join(__dirname, '../server/index.ts') },
externalsPresets: { node: true }, //相当于 target:node
externals: [
//新增
nodeExternals({
allowlist: ['antd-mobile']
})
],
output: {
filename: 'bundle.js',
publicPath: '',
path: path.resolve(__dirname, '../target'),
globalObject: `typeof self !== 'undefined' ? self : this`
},
resolve: {
alias: {
'@': path.resolve(__dirname, '../src')
},
extensions: ['.ts', '.tsx', '.js', '.json']
},
module: {
rules: [
//babel 配置
{
test: /\.tsx?$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
[
'@babel/preset-react',
{
runtime: 'automatic'
}
],
'@babel/preset-typescript'
],
plugins: [
[
'@babel/plugin-transform-runtime',
{
corejs: 3
}
],
[noRequireCss],
['dynamic-import-node'],
['@loadable/babel-plugin']
]
}
},
exclude: /node_modules/ //排除 node_modules 目录
},
//新增
//图片
{
test: /\.(png|jpg|gif|svg|svg+xml)$/i,
type: 'asset'
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset'
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
//less
{
test: /\.less$/i,
use: [
'style-loader',
'css-loader',
'postcss-loader',
{
loader: 'less-loader',
options: {
lessOptions: {
strictMath: true
}
}
}
]
}
]
},
plugins: [new CleanWebpackPlugin()]
};
同时我取消了对于node环境的判断 announcement.tsx
import { InfiniteScroll } from 'antd-mobile';
import produce from 'immer';
import { useState } from 'react';
import siteObj from '@/store/site';
import {
annoucementList,
AnnouncementList,
AnnouncementListParams
} from '@/api/announcement';
import './style.less';
const prefix = 'announcement';
export default function Announcement() {
const [hasMore, setHasMore] = useState(true);
const [offset, setOffset] = useState(0);
const [announcement, setAnnouncement] = useState<AnnouncementList>({
rows: [],
total: 0
});
const loadMore = async () => {
const data: AnnouncementListParams = {
site: siteObj.site,
limit: 5,
offset
};
const res = await annoucementList(data);
console.log(res.data);
const copy = produce(announcement, draft => {
draft.rows = draft.rows.concat(res.data.rows);
draft.total = res.data.total;
});
setHasMore(copy.rows.length !== copy.total);
setOffset(copy.rows.length);
setAnnouncement(copy);
};
return (
<div className={prefix}>
<div>一些我应该能看到的文字</div>
{announcement.rows.map(item => (
<RenderAnnouncement key={item._id} data={item} />
))}
<InfiniteScroll loadMore={loadMore} hasMore={hasMore} />
</div>
);
// 服务端渲染的时候 我们应该返回的静态HTML
}
如果我将
{announcement.rows.map(item => (
<RenderAnnouncement key={item._id} data={item} />
))}
<InfiniteScroll loadMore={loadMore} hasMore={hasMore} />
注释掉 那么首屏进入可以正常访问 并且查看源代码可以看到
我注释掉了mobx的引入 确保不是mobx的问题
不能用style-loader,要用isomorphic-style-loader
@zyw00001 Ok, Thanks a lot! 它终于工作正常了 我总结一下: 问题的关键在于antd-mobile进行服务端渲染的时候 需要使用webpack-node-externals 排除 并且引入isomorphic-style-loader将它的样式插入到html中
看起来这应该与antd-mobile的打包方式相关 使用antd的时候没有此问题 因为antd需要引入一个css文件
版本信息:
os: win10 node: v14.17.5 antd-mobile: ^5.0.0-beta.15 @loadable/component: ^5.15.0 @loadable/server: ^5.15.1
src/app.tsx
src/index.tsx
server/index.ts
server/route.tsx
webpack.client.ts
webpack.server.ts
noRequireCss
运行
yarn run client
yarn run serve
node target/bundle.js
报错